Skip to content

Release v1.1.0 - Design, memory and native Mission scheduling - #1395

Draft
pascalandr wants to merge 159 commits into
mainfrom
dev
Draft

pascalandr wants to merge 159 commits into
mainfrom
dev

Conversation

@pascalandr

@pascalandr pascalandr commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

CodeNomad 1.1.0 — Design, memory and native Mission scheduling

CodeNomad 1.1 brings image creation into a dedicated Design workspace, adds optional local agent memory, and gives Missions native scheduling and a clearer task tree. Native integrations and provider usage controls also become easier to manage from Status.

Design and Images

Optional persistent agent memory

  • Enable Memory to retain useful facts, preferences and references across conversations. Separate About you and Project scopes keep personal and project knowledge distinct; a repository's worktrees share project memory (feat(memory): add opt-in persistent agent memory #1313).
  • Review and edit entries in Settings, keep a local change history, and ask the agent to tidy a scope. Memory is local and off by default; disabling it retains saved entries and their history.

More capable Missions

Native integrations and Status

Everyday and desktop improvements

Upgrade notes

Full changelog: v1.0.1...v1.1.0

…1194)

## Summary

- New **Support CodeNomad** help page (10 languages): what sponsorship
pays for, the funding policy (a contribution, not a purchase; never
required to download, use or update) and one button per funding channel
naming who receives the money.
- **Download page** ends with a short *Support development* section with
the same buttons; the help **header** gains a *Sponsor* link; the former
support page is retitled **Bug reports and source builds** (ids
unchanged) so the two are not confused.
- **App**: *Support CodeNomad* in Settings → Info (secondary button
between Updates and Diagnostics), in the Help menu after *CodeNomad
Help* (Electron and Tauri, English help-page fallback) and as a palette
command without a default shortcut. All open the localized help page.
- **Repository**: `.github/FUNDING.yml` lists the maintainer's GitHub
Sponsors profile and the Support page (the Sponsor button needs
*Settings → General → Features → Sponsorships*).

Channels live in `docs/help/funding.json` and render through a
`{{funding}}` directive allowed only on the download and sponsor pages,
so adding a channel needs no retranslation. Entries are validated (exact
keys, unique ids, `https://github.com/sponsors/<login>` only) and
escaped; a page using `{{funding}}` with no channel fails the build. CSP
unchanged.

Agreed with the co-maintainer before publishing.

## Validation

- `npm run test:help`: 31/31 (27 pages × 10 locales, funding validation
and placement).
- `node scripts/test-help-browser.mjs`: 47/47 (header overflow at 390 px
in en/he/ne/ja/zh-Hans, button geometry).
- `npm run typecheck`; unit tests help-url, hotkey-bindings,
menu-labels; browser tests help-command, server-info, view-menu-electron
(22/22); `cargo test` for the Tauri crate (291/291, including the
English sponsor fallback).
- Independent review: zero findings.
… the last clone's location (#1195)

The clone dialog fills Destination folder as the repository's `git clone`
folder name (HTTPS, scp-style and local sources, including `/.git`
sources) inside a parent: the folder chosen with Browse, which now picks
the folder that will contain the clone; else the folder above the last
successful clone; else the folder above the most recent project. A typed
destination is kept and sent unchanged, so the clone's folder can be
renamed by hand; its parent is what the next clone remembers. The picker
opens on the parent of the current destination. Drive, filesystem and UNC
share roots keep their trailing separator, and `#` stays part of local
paths.

The parent is remembered per window at once and saved in the
server-scoped `ui` state (`lastCloneParent`) for other windows and
restarts; saves are chained so the last clone's parent is kept, and the
chain is tracked by `storage.trackWrite` so native shutdown waits for
queued saves.

The picker description, the Projects help in all ten locales and
dev-docs/PROJECTS.md are updated. clone-destination.test.ts and
preferences-clone-parent.test.ts (added to CI) and a new launcher browser
test cover suggestions, Browse, typed paths, pending saves and the
shutdown drain.
…instead of a broken icon (#1196)

## Problem

Agents often answer with a Markdown image of a file on their machine,
e.g. after OpenCode's `image_generate` tool:

```markdown
![Un petit mouton à l’aquarelle](C:/Users/Admin/AppData/Local/Temp/opencode/images/ses_…/image_….png)
```

The renderer cannot load a local path (`C:` is read as a URL scheme), so
the transcript showed a broken-image icon, even though the same image
was already displayed a few rows above by the `read` tool that loaded
that file.

## Change

- `lib/markdown.ts`: a Markdown image whose source is a local path
(`C:/…`, `C:\…`, `file://`, absolute or relative POSIX paths) renders as
its alt text (or the file name) in a `span.markdown-local-image`, never
as a broken `<img>`. Remote, `data:` and other URL sources keep marked's
default rendering.
- `components/markdown.tsx` + new `lib/markdown-local-images.ts`: blocks
containing such a label look up a completed tool image of the same path
among the session's loaded messages (the image's file name, or its
single-image tool's `filePath`; Windows paths compare
case-insensitively; a relative path must match exactly one known path's
end) and swap the label for an `<img>` with that image's existing source
(`data:` URI or CodeNomad's session-media path). The lookup tracks the
store, so an image arriving after the answer resolves it in place.
Blocks without a local image never read the session's parts.
- No new backend route: nothing outside the transcript's own images is
ever loaded. An unmatched path keeps its alt text.
- `dev-docs/TRANSCRIPT.md` documents the rule; styles in
`styles/markdown.css`.

## Validation

- Unit (`markdown-local-images.test.ts`): path recognition (Windows,
UNC-free drive paths, `file://`, POSIX, relative, schemes left alone),
matching rules, candidate collection from completed tool parts (running
and unsafe images ignored), and rendering (alt text, file-name fallback,
remote images unchanged, hostile alt/path text escaped). Existing
`markdown.test.ts` passes.
- Browser (`tool-images.test.ts`, real `Markdown` component, store and
history load): an answer without the read result shows alt text and no
`<img>`; once the read tool's image of that path is loaded, the same
answer shows the decoded image while an unknown path keeps its alt text;
no overflow at 380 px. The other five image tests still pass.
- `npm run typecheck --workspace @codenomad/ui`.
The automation-forms test where an agent fills the real Mission editor
failed intermittently at its Create step since the project nesting depth
editor joined the creation Options. Opening Options starts a depth read
that showed a "Saving..." status, then swapped it for the result; each
swap pushed the footer down about 100 ms later, so native automation
measured Create, the layout moved and the click landed on the footer,
and nothing was submitted. The status also claimed a save during a read.

The depth editor now shows its busy status only while confirming or
saving; a read keeps the field read-only without announcing anything.
In the panel, the creation form fills the height below the header and
its footer sticks to the panel's bottom edge, so Create keeps its place
while depth reads, catalogs or alerts resize the body.

automation-forms answers the depth read like a native runtime and waits
for the effective depth before clicking Create, as an agent would, and
mission-control checks that a read only locks the field while a write
announces Saving. The target test passed 8/8 including a cold first run
(the old test failed 3/8 on dev), Create stayed in place across read
timings and panel sizes, and every Missions, editor, preferences and
drawer browser suite and the UI typecheck pass.
## Summary

- `CODE_OF_CONDUCT.md`: Contributor Covenant 2.1; reports go privately
to a maintainer.
- `GOVERNANCE.md`: maintainers and roles, decisions (significant ones
need both maintainers), joining and leaving, current funding channel, no
self-approved expenses from shared funds, succession.
- `SECURITY.md`: supported versions, private reporting through GitHub
with a maintainer-contact fallback, what to include.
- `CONTRIBUTING.md` links both.

Private vulnerability reporting must be enabled by a repository admin
(Settings, Security) for the main reporting route.

## Validation

Links and facts checked; independent review: zero findings.
… help (#1198)

The **Image generation** info tip (Settings → Providers → Images) and
the help's Images section said agents ask for permission before each
image. OpenCode's default agents allow `image_generate` without asking
(the Build agent's rules start with `* → allow`), so the promise was
inaccurate; whether an agent asks is governed by the user's own OpenCode
permissions, as for any tool.

The tip now reads "Each image is a paid request." and the help keeps the
background-job and saved-file behavior, in all ten locales. Copy-only
change: help checks and UI typecheck pass.
…ges help (#1199)

The **Image generation** info tip (Settings → Providers → Images) kept
the sentence "Each image is a paid request." after #1198. It is
inaccurate too: image models can be free or local. The tip now reads
"Lets agents create and edit images with an image model from a connected
provider. Applies to every project." and the help's Images section says
each image is generated in the background and saved as a file, in all
ten locales. Help checks and UI typecheck pass.
…own experimental block (#1200)

## Problem

OpenCode reads `experimental.media_blob` from the whole `experimental`
object of the highest-priority configuration document that has one
(upstream: anomalyco/opencode#60410). A project
`.opencode/opencode.jsonc` holding only
`{"experimental":{"subagent_depth":8}}` — exactly what the Missions
depth editor writes — turned **Store images once** off in that project
even though the Global box was checked. Settings only showed a caption
("Another OpenCode configuration's experimental settings take precedence
here: images stay inside each message.").

## Change

- **Images** (`opencode/image-settings.ts`): when the selected
Location's own project document (the project write target) decides
`media_blob` through its existing `experimental` object, the snapshot
reports `mediaStorage.project`, the box shows the effective value with
no caption, and saving writes `media_blob` to the Global document
**and** to that project block. A block that disappeared after reading is
never recreated (conflict). Image generation is read per field and still
writes Global only. The caption remains for deciding documents CodeNomad
does not edit.
- **Subagent depth Inherit** (`opencode/subagent-depth-settings.ts`):
removes, in one comment-preserving edit, a project `experimental` object
left empty or holding only a `media_blob` equal to the value removal
exposes. That value counts only when certain (runtime declares
`media_blob`; the next native object belongs to the Global write target
and deep-equals its fresh document); otherwise the object stays, which
hides nothing new. `removeEmptyNativeObject` takes a removability
predicate; `samePath`/`maskedBy` move to
`opencode/experimental-layers.ts`.
- The depth editor writes only the depth. Predicting the inherited
object for a newly created project block needs OpenCode's full layer
order (explicit, managed, content, normalization, watcher lag) and stays
out of scope; the root cause is upstream (#60410), and Images now shows
and fixes the per-project value in one click.
- Docs: `dev-docs/SETTINGS.md`, `dev-docs/MISSIONS.md`, help
`providers.md` and `missions.md` in 10 locales.

## Validation

- `image-settings.test.ts`, `subagent-depth-settings.test.ts` (6/6):
project write, comment preservation, disappeared-block refusal, Inherit
cleanup with BOM/CRLF comments, kept block for lagging/normalized
entries, no Global object, missing capability
- `websearch-settings.test.ts` browser test (4/4); the new Images
assertion fails without the card change
- `scripts/test-subagent-depth-settings-native.mjs` (Global image
storage cleanup) and `scripts/test-image-settings-native.mjs
--expect-supported` against an isolated OpenCode 2.2.1 CLI
- `scripts/test-help.mjs` (31/31), server and UI typecheck, `git diff
--check`
…ent (#1201)

Slice 2 ("content") of the Design image companion (SCR v1.3.1 §5, §11.2,
§15; AC-14, AC-15, AC-49 content part).

## What

- Native OpenCode files under
`packages/server/src/opencode/design/opencode/`: the `design` primary
agent and seven unlisted skills (`design-core` + six recipes), with
per-file licence notices, `NOTICE.md` and the Apache-2.0 licence. They
work unchanged when copied into a project's or the global `.opencode/`.
- `design/content.ts`: `loadDesignContent()` (text embedded through a
`.md` loader added to the shared native plugin build) and strict
`parseDesignContent()` for checkout code and tests.
- Nothing registers or displays the content yet: the activation slice
wires it into the session-pruning plugin.

## Attribution

- `design-logo` ← brandkit (MIT); `design-ui-mockup` ←
imagegen-frontend-web/mobile (MIT); full MIT notices kept in the files.
- `design-core` and the agent ← Open Design's review ideas, the five
directions (rewritten, no brand names) and the image-poster prompt order
(Apache-2.0, marked as modified work).
- Other recipes written for CodeNomad. No CC-BY prompts, no design
systems.

## Tests

- `node --import tsx --test
packages/server/src/opencode/design/content.test.ts` (9/9; verified
against 19 content mutations)
- `node --test packages/server/scripts/native-plugin-build.test.mjs`
(2/2, self-contained bundle outside the checkout)
- `node --import tsx scripts/test-design-content-native.mjs <cli>`
against OpenCode 2.2.1 and 2.1.2 (added to the PR build); outside CI it
requires `CODENOMAD_NATIVE_FIXTURE_PARENT` outside the user's home
because project discovery walks every ancestor
- Server typecheck; `build:pruning`, `build:automation`,
`build:missions`
…iven by backend leases (#1202)

Slice 1 ("activation-agent") of the Design image companion (SCR v1.3.1
§11, decisions T1/T3/T6/T8; AC-1 to AC-7, AC-57, AC-49 installed-bundle
part).

## What

- **Backend request in leases** (`opencode/design/activation.ts`,
`demand.ts`, `desktop-plugin-installation.ts`,
`desktop-plugin-lifecycle.ts`): every retained session-pruning lease
carries `{"version":1,"design":true}` while an installed, enabled panel
extension declares `opencode.designAgent`; atomic rewrites only on
change, serialized with the heartbeat; new installations start from the
current state. Nothing can declare the permission until extension API 4
(slice 5), so Design stays inactive in production for now.
- **Load-time registration** (`opencode/design/register.ts`,
`session-pruning/desktop-plugin.ts`): agent and skill transforms
registered once while the plugin loads, before `followPresence`;
reconciliation flips a Location-local flag and reloads only on an actual
change of the union of live requesters. User's own `design` / `design-*`
win; their permissions apply; content (`loadDesignContent()` from #1201)
loads lazily. Capability-gated on agent/skill transforms.
- **Shared observation** (`desktop-plugin-presence.ts`): v3 per-process
snapshot with bounded (256 B) lease reads; `observeDesignRequest`.
- **Status** (`opencode/design/status.ts`): `designAgentStatus` →
`present` (content version) / `user` / `absent` from `GET /api/agent`.
- Docs: `dev-docs/OPENCODE_COMPATIBILITY.md`,
`dev-docs/SESSION_PRUNING_RPC.md`.

## Tests

- Unit: `node --import tsx --test
packages/server/src/opencode/design/*.test.ts
packages/server/src/opencode/session-pruning/*.test.ts
packages/server/src/opencode/desktop-plugin*.test.ts`
- Native (PR build): `node scripts/test-design-activation-native.mjs
<cli>` after `npm run build:pruning`, against isolated OpenCode 2.2.1
and 2.1.2 with fake chat/image models (outside CI with
`CODENOMAD_NATIVE_FIXTURE_PARENT` outside the home). 2.2.1: activation
≈1.9 s, one ask under Design and Build, late Location < 1 s,
single-requester expiry ≈16 s, disable ≈2 s, replaced shim/bundle keeps
priority and permissions.
- Server typecheck; `packages/server/src/index.ts` ≈789 lines (+5).
…sion-pruning plugin (#1203)

Slice 3 ("history-gallery") of the Design image companion (SCR v1.3.1
§10; AC-17..AC-21, AC-23, AC-50, AC-51, AC-56, server part of
AC-9/AC-10; AC-22 except the full-size `not-previewable` decode bound,
which belongs to the API 4 channel in slice 5 per §10.1).

## What

Four fixed session-pruning RPCs (documented in
`dev-docs/SESSION_PRUNING_RPC.md`):

- `images`: bounded pages of the conversation's image `read` parts
(pixels targets) and `image_generate` calls (native states);
prompt/model/parent only when proven by the execution-interval rule,
else "unverifiable" with pixels; prompts only for a marked root on host
request.
- `imageRead`: bytes of a read image part only; reuses the asset reader,
split into `storedAsset` / `assetBytes` (`assetRead` unchanged).
- `imageReference`: prefill target validation with the post-write check,
including a parallel generation to the same file still without a notice.
- `imagesConversation`: authoritative lookup of the Location's root,
unarchived, marked conversation (incomplete search → unknown),
open/create under a process lock, new on request.

## Notes

- Cursor binding includes an `"images"` label so an `assets` cursor
cannot be replayed.
- `imageCount` / `lastUsed` are composed by the host in slice 5.
- Consumed native contracts: the `Saved image: … (` notice text and tool
`metadata.path` / `jobID` / `model`; a change degrades to
unknown/unverifiable.

## Tests

- `node --import tsx --test
packages/server/src/opencode/session-pruning/*.test.ts
packages/server/src/server/routes/session-pruning.test.ts
packages/server/src/server/__tests__/instance-proxy.test.ts
packages/server/src/server/routes/panel-extension-assets.test.ts`
(297/297): 30 ported prototype mutations on two real 2.2.1 transcripts
with/without media_blob; 9 reintroduced regressions detected.
- `node scripts/test-session-pruning-native.mjs <cli>` (isolated 2.2.1):
Images conversation lookup/open/new, found again after a daemon restart.
- Design tests, server typecheck, `build:pruning`.
- `scripts/test-session-pruning-native.mjs` ≈453 lines.
…nd exclude its images from Git (#1204)

Slice 4 ("conversation-ui") of the Design image companion (SCR v1.3.1
§7.3, §7.4, §12, T8; AC-8, AC-11, AC-12, AC-13).

## What

- **Route** `POST /api/workspaces/:id/images-conversation` (lookup /
open / new) over the session-pruning `imagesConversation` RPC, with
Location ownership, the worktree deletion fence and `blocked:
unavailable` for older plugins.
- **Git exclusion** of `.codenomad/design/` through the execution host's
Git (WSL distro Git for WSL Locations), reporting `excluded` /
`not-repository` / `unavailable`; the shared helper now appends instead
of rewriting, preserving foreign bytes; `.gitignore` never touched.
- **UI** `resolveImagesConversation` + `showImagesConversation`:
localized title "Images", `agent: "design"` only when present,
CodeNomad-resolved model at creation, found conversation keeps its
model, composer focused, nothing sent.
- **Composer**: "The Design agent is not available" + "Use the default
agent", blocking send only when the selected agent is `design` and
missing (T8). 10 locales.
- Docs: `dev-docs/COMPOSER.md`, `dev-docs/SESSION_PRUNING_RPC.md`.

## Help

Nothing in this slice is reachable without the Design panel (API 4 /
extension slices), so `docs/help` is unchanged here; the Design help
section lands with the panel.

## Tests

- Server: `design-git-exclude`, `images-conversation` route,
`git-process`, `worktree-inventory`, `native-worktree-custody`,
`session-pruning`, `assets-conversation` (38/38, including a real WSL
Ubuntu project and linked worktree with
`CODENOMAD_TEST_WSL_DISTRO=Ubuntu`).
- Browser: `images-conversation` (3/3) plus composer/session regressions
(`composer-alternate-send`, `composer-draft-safety`,
`composer-resume-typing`, `session-fork`, `session-create-feedback`,
`interruption-dock`).
- UI and server typecheck; `node scripts/test-help.mjs`.
- Long files touched: `prompt-input.tsx` ≈1041, `http-server.ts` ≈2488,
`api-client.ts` ≈857, `session-view.tsx` ≈726.
…anion (#1205)

Slice 5 ("api4") of the Design image companion (SCR v1.3.1 §9, T2, T5,
T7; AC-24..AC-33, AC-42, AC-53; plus items deferred by slices 1–4:
`opencode.designAgent` permission, full-size decode bound,
`imageCount`/`lastUsed`, host deadline).

## What

- **API 4 schema** (`panel-extensions/contract.ts`, `archive.ts`):
`session.context` + `session.images.read`, `session.images.prompts`
(requires read), `images.conversation`, `composer.prefill`,
`panel.preferences`, `opencode.designAgent`. APIs 1–3 unchanged.
- **Install sentences** before the trust checkbox (10 locales).
- **Fixed routes** (`server/routes/panel-extension-api4.ts`) with
permission checks before and after reads; 19 s server stop, 8 s per
daemon call, aborted with the frame.
- **Channels** (`ui/.../api4-channel.ts`): 19.5 s deadline, 4 requests
per mount, late results dropped, coalesced invalidations, full-size
`not-previewable` bound.
- **`conversation.status()`** read-only (exclusion checked as Git reads
lines; `imageCount` exact or `null`); open/startNew append.
- **Prefill** (`composer-prefill.ts`): composer rules on the resulting
draft, `{{n}}` references revalidated and cited as text, never sends,
1/s.
- **Preferences**: 4 KiB JSON per extension in the existing store;
corrupt store → unavailable, untouched.
- Docs: `PANEL_EXTENSIONS.md#api-4`, `COMPOSER.md`; help `extensions.md`
in 10 locales.

The panel-side "Design agent missing" warning is rendered by the Design
extension (host exposes `designAgent`).

## Tests

- Server: 83 passed (+ real WSL Ubuntu exclusion check with
`CODENOMAD_TEST_WSL_DISTRO=Ubuntu`).
- Browser: `panel-extension-api4` 10, `panel-extensions` 14,
`images-conversation` 4, composer 12.
- UI unit 7; UI and server typecheck; help 31/31.
- Long files touched: `prompt-input.tsx` ≈948, `http-server.ts` ≈2300
(one line each).
Slice 8 (part a) of the Design image companion (SCR v1.3.1 §18; AC-45,
AC-47).

- `docs/help/<10 locales>/extensions.md#design` (+
`#design-conversation`, `#design-light-table`,
`#design-without-codenomad`), using the merged UI labels and the
extension's labels.
- One link sentence at the end of `providers.md#images`; existing Images
texts unchanged; no new permission/consent wording.
- `dev-docs/PANEL_EXTENSIONS.md` and
`dev-docs/OPENCODE_COMPATIBILITY.md` touch-ups; COMPOSER,
SESSION_PRUNING_RPC and AGENTS already describe the delivered state.

**Merge timing:** the help says to install Design from the catalogue, so
this merges together with the catalogue publication.

Checks: `node scripts/test-help.mjs` (31/31), `node
scripts/build-help.mjs` (27 pages × 10 languages), `node
scripts/test-help-browser.mjs` (47/47).
…andbox on native hosts (#1208)

Slice 6 (qa-native) of the Design image companion (SCR v1.3.1; AC-16,
AC-41, AC-52, AC-54). Test code only; no product source changes.

- `scripts/test-design-companion-native.mjs`: isolated OpenCode daemon,
scripted chat model, fake image model; AC-16 and native AC-52 (re-read
35 assistant messages later). Added to the PR build on
Windows/Ubuntu/macOS.
- `--ui <Design ZIP>` → `scripts/test-design-companion-ui.mjs` +
`fixtures/design-companion.tsx`: real UI S8 path with the installed
extension and real-transcript Markdown rendering (AC-52, AC-54).
- `scripts/panel-extension-native-probe.mjs`,
`test-panel-extension-native.mjs --api4` (also off Windows), new
`test-panel-extension-electron.mjs`: sandbox invariants with negative
controls (AC-41).
- `fixture-exemptions.ts`: registers the Images conversation and API 4
browser tests required by `fixture-wiring`.

Runs: Windows (OpenCode 2.2.1 native + UI path; WebView2 and Electron
runners), Ubuntu VM (WebKitGTK and Electron runners). **Not run:** macOS
WKWebView — the VM stops at its login screen.
…ct (#1209)

## Summary

- OpenCode **2.2.3** becomes both the **technical minimum** and the
**recommended** runtime. The minimum moves because upstream
[#60258](anomalyco/opencode#60258) (shipped in
2.2.2) changed the image tool contract that CodeNomad's Images gallery,
Design agent and Images settings consume: one name,
`experimental_image_generate`, for the tool, its permission action, its
job type and its completion notices' `metadata.source`, and the tool on
by default (only `experimental.image_generate: false` disables it). On
2.2.2+, the gallery lost every generation entry and the Design companion
fixture timed out; 2.2.1 passed.
- Images are implemented for the 2.2.3 contract only, with no fallback
to the old name: `IMAGE_TOOL` drives the gallery and its proof;
`imageGenerationSelection` reads unset as on; the Design agent and
skills name the new tool; the activation fixture exercises the image
tool instead of falling back to webfetch; the gallery fixture represents
the 2.2.3 transcript format.
- Older daemons (2.1.x, 2.2.1, 2.2.2) go through the existing
below-minimum path (`native_contract`). The 2.1.0 client/plugin/schema
pins stay.
- Help (all ten locales), READMEs, `CONTRIBUTING.md`, `dev-docs`
(compatibility, pruning, panel extensions, settings) and the
architecture skill describe the new minimum, its reason and default-on
image generation. The setup minimum-reason UI string is updated in all
locales.
## Contract review (v2.2.1 → v2.2.2 → v2.2.3; 40 + 101 commits)

Reviewed from the exact tags `v2.2.1` (`3ebcf9a32c`), `v2.2.2`
(`098aa7fd6e`) and `v2.2.3` (`082ac6281f`), the published npm packages,
and an authenticated OpenAPI dump from each official CLI running in its
own sandbox. The [changelog](https://opencode.ai/changelog) has entries
for both 2.2.2 (29 October, highlights only) and 2.2.3 (30 October, full
notes); everything below was also checked against the commit log and
source.

**Headline:** the HTTP contract CodeNomad consumes didn't change, but
2.2.2 **renamed the image tool** from `image_generate` to
`experimental_image_generate` and **turned it on by default**
([#60258](anomalyco/opencode#60258)).
CodeNomad's Images/Design features match the old name and the old
default, so they break on 2.2.2 and later (see [Design companion
finding](#design-companion-finding)). Nothing else found affects
CodeNomad.

- **Authenticated OpenAPI:** **134 paths and 162 operations** on all
three, the same operation set. Schemas 355 → 350 → 351.
  - **Added/removed operations:** none in either release.
- **Changed operations (2.2.2):** `GET /api/model`, `GET /api/event`,
`PUT /api/pty/{ptyID}`, `POST /api/shell`, `GET /api/shell/{id}/output`,
`POST /api/debug/heap-dump` and `GET /api/experimental/migration/v1`.
All of these are **representation-only**: the Effect 4.0.6 JSON Schema
output flattens nested `anyOf`, bounds integers with `maximum:
9007199254740991` (and `minimum` for signed integers), and rewrites the
numeric-string query pattern. The event-type and model-type enums are
unchanged.
- **Changed operations (2.2.3):** only the description of `POST
/api/credential`, which can now be refused by an `integration.connect`
policy.
- **Consumed schemas:** the same family selector as the 2.2.1 review
(`V2EventEncoded`, `Session.*`, `SessionStats.*`, `Form.*`,
`Permission.*`, `Prompt*`, `Event.*`, `Location.*`, PTY/Shell).
Re-derived from that description it selects 104 schemas at 2.2.1 (the
2.2.1 file says 103), all byte-identical from 2.1.4 to 2.2.1.
- In 2.2.2 the seven alias copies `Form.Fields_1..3` and
`Session.Message.Native_1..4` collapse into their base schemas, which
are byte-identical to the copies, leaving **97**. Of these, 71 are
byte-identical and 24 differ only in generator representation.
- Two changes are additive and optional: `Session.Info.time.busy`, the
start of the latest execution
([#59910](anomalyco/opencode#59910)), and the
input field `PromptInput.FileAttachment.source` (`Prompt.FileSource`,
inline or uri; CodeNomad doesn't send it).
- `V2EventEncoded`, `Session.Message.*` content, `Session.Inbox.*`,
`Form.*` semantics, `Permission.*` and `Event.SeqEncoded` are unchanged.
    - From 2.2.2 to 2.2.3 the family is byte-identical.
- **`Config.InfoEncoded`:** no semantic change in 2.2.2 (representation
only). 2.2.3 extends the managed-policy `action` enum (top-level and
`experimental.policies`) with `integration.connect`, `mcp.use`,
`skill.use` and `plugin.use`
([#60315](anomalyco/opencode#60315)); CodeNomad
writes no policies. The *meaning* of `experimental.image_generate`
changed: unset now means on, and only `false` turns it off. The schema
shape stayed the same and the OpenAPI carries no description, so no
contract signal reveals it.
- **Schedules (not consumed on dev):** `when` becomes optional;
`if`/`until` conditions (shell in 2.2.2, Code Mode in 2.2.3) are added;
outcomes gain `checked`/`expired`.
- **Admission:** every predicate in `compatibility/negotiate.ts`
(unchanged since the 2.2.1 review) holds on 2.2.1, 2.2.2 and 2.2.3.
- **Instance proxy:** no new routes and an unchanged allowlist. Proxied
routes whose representation changed: `GET /api/model`, `PUT
/api/pty/{id}`, `POST /api/shell` and `GET /api/shell/{id}/output`.
Integer bounds and an equivalent numeric pattern accept everything
CodeNomad sends.
- **Published declarations** (2.2.1 → 2.2.3):
- **Client:** 185 methods on all three; the method tree is identical.
2.2.2 adds the generated types `ScheduleCondition*`; `FormFields2` is
renamed to `FormFields1`, which is generated-type churn. 2.2.3 adds the
Promise media helpers `withMediaRefs`, `mediaHashFromUri` and
`MediaRequestOptions`
([#60116](anomalyco/opencode#60116)) and
persistent-PTY frame helpers in `solid/pty`. CodeNomad imports none of
these.
- **Schema:** 2.2.2 adds schedule conditions (`ShellCondition`,
`CheckMode`, …) and exports `SessionLegacy.part`. 2.2.3 adds
`CodeCondition*` and `policy.Action`.
- **Plugin:** 2.2.2 adds the GUI
`ComposerQuoteAnchor`/`ComposerFocusOptions`. 2.2.3 has no export
changes, but the optional peers `@opentui/*` become `>=0.8.0`
([#60385](anomalyco/opencode#60385)). The
published `dependencies.effect` moves from **4.0.1 to 4.0.6** (2.2.2).
- **Typecheck:** the server and UI typecheck with **0 errors** against
the 2.2.2 and 2.2.3 client/schema/plugin/effect declarations through
scratch `paths` overrides. The pinned 2.1.0 control is also clean. The
harness now also remaps `effect` itself; the 2.2.1 harness skipped it
because effect's `exports` are plain `.js` strings.
- **Host modules:** the three daemon-loaded bundles still import
`@opencode/plugin` (`Plugin`), `/effect` (`Plugin`, `Rpc`, `Location`),
`/promise/adapter` (`fromPromise`), `/rpc` (`Rpc`), `effect` and
`effect/encoding` (`Base64Url`) from the host. A probe plugin in an
isolated **2.2.3** daemon resolves every module and binding, as on
2.2.1. `runtime-modules.ts` is unchanged between the tags.
- **Host Effect: 4.0.1 → 4.0.6** (2.2.2,
[#59630](anomalyco/opencode#59630)). The 2.1.0
`@opencode/schema`/`@opencode/client` code bundled into the Missions and
pruning plugins now runs on host Effect 4.0.6. The plugin-runtime,
pruning, Missions-contract, permission-receipts and image-settings
native suites pass on 2.2.3.
- **Private native services:** the keys (`LocationServiceMap`, `Form`,
`Permission`, `Location`, `Job`, `Database`, `Session`, `Bus`) and the
members CodeNomad calls are unchanged. The changes are additive:
`Permission.Unattended` (a reference that denies unanswerable asks for
schedule checks), a `CorrectedError.message` getter, and `Session`
deciding fork media from the parent's location.
- **Storage** (three migrations): `session_part` (new table),
`session_v2.time_busy` and four `schedule` check columns.
- **`session_part`**
([#58915](anomalyco/opencode#58915)): history
reads now *prefer* `session_part` rows over
`session_message.data.content` for assistant messages. The projector
writes parts there only for messages that already have part rows, so new
messages stay inline. Every 2.2.3 fixture DB inspected has **0**
`session_part` rows, and upstream `dev` (`1e28374e48`, 30 October) has
no writer either.
  - The tables CodeNomad reads directly keep their shape.
- **Forward risk:** when a release starts writing parts, CodeNomad's
direct `session_message.data.content` readers and the pruning `UPDATE …
json_set(data,'$.content',…)` (`session-pruning/transaction.ts`) would
silently miss or be shadowed by part rows. See Recommendations.
- **Behavior changes checked:**
- **Image generation** (2.2.2): renamed and on by default. See the
[dedicated section](#design-companion-finding).
- **Code Mode:** inner tool progress no longer replaces the `execute`
call rows ([#60480](anomalyco/opencode#60480)),
and progress is throttled to at most one update per 100 ms
([#60120](anomalyco/opencode#60120)). Programs
see the same failure text as direct calls
([#60109](anomalyco/opencode#60109)), `fetch` in
Code Mode is now subject to `webfetch` permissions
([#59954](anomalyco/opencode#59954)), `for…in`
visits inherited keys
([#59933](anomalyco/opencode#59933)) and `null`
survives text encoding
([#57639](anomalyco/opencode#57639)). The
run-to-completion regression behind the 2.1.2 minimum passes on 2.2.3:
`{ total: 2000, beforeAwait: 1000, count: 1000 }`.
- **Permissions and MCP:** permission reasons and feedback are kept in
MCP tool failures
([#60102](anomalyco/opencode#60102)). Shell
permission checks moved fully off tree-sitter and now check redirects,
PowerShell method calls and dynamic directory changes
([#60332](anomalyco/opencode#60332),
[#60376](anomalyco/opencode#60376)), so some
shell commands may now ask where they didn't. The managed-policy split
(`integration.use` vs `mcp.use`/`skill.use`/`plugin.use`) is a changelog
"breaking change" for managed deployments only.
- **Sessions:** deleting a session removes its persistent terminals
([#60322](anomalyco/opencode#60322)), and a
persistent terminal with a missing cwd is rejected
([#60300](anomalyco/opencode#60300)). Forked and
imported sessions store media as blobs when `media_blob` is on
([#60118](anomalyco/opencode#60118)). The native
fork suite passes on 2.2.3.
- **Providers:** a built-in **Claude Agent** provider uses the user's
Claude Code login
([#58782](anomalyco/opencode#58782)).
Plugin-supplied model variants are preserved
([#60097](anomalyco/opencode#60097)), and the
dynamic provider npm fallback runs after external SDK hooks
([#60380](anomalyco/opencode#60380)). These are
source-reviewed and touch no consumed contract.
- **Persistent PTY binary** (the process daemon): 2.2.2 switches from
`@opencode-ai/pty` 0.3.1 to `@opencode/pty` **0.3.3**
([#60276](anomalyco/opencode#60276)).
`process-daemon-v0.3.3` contains only the release commit after 0.3.2 (a
version bump of the `@opencode/pty-*` binaries). **v0.4.0** (terminal
renaming, [#60324](anomalyco/opencode#60324)) is
**not** shipped in CLI 2.2.3, which pins 0.3.3. CodeNomad only lists
persistent PTYs (worktree deletion guard), whose schema is unchanged
apart from representation.
- **Client fix not inherited:** the 2.2.3 client evicts missing sessions
on 404 ([#60459](anomalyco/opencode#60459)).
CodeNomad stays on the 2.1.0 client.
- **`@opencode/ai` breaking changes** (media moved to `/experimental`,
one `tool()`): CodeNomad doesn't import `@opencode/ai`.
- Desktop/TUI-only changes (timeline performance, side-panel tabs,
spellcheck, question sounds, OpenTUI 0.8) don't affect CodeNomad.

**Verdict:**

- The **2.1.0 client/plugin pins stay**. Bumping them would bring
nothing CodeNomad consumes.
- No consumed HTTP/plugin contract broke; the breaking change is the
image tool contract of #60258, consumed by the Images gallery, the
Design agent and the Images settings.
- **Decision applied in this PR:** 2.2.3 becomes both the minimum and
the recommendation, and Images are implemented for the 2.2.3 contract
only (see Summary). This replaces the review's initial suggestion to
keep the 2.1.2 minimum and accept both tool names.

**Not verified here:**
- the full native suite matrix on 2.2.2/2.2.3; only the runs in Appendix
I were performed;
- the forward-compatibility of the `session_part` readers against a
runtime that writes parts (no such runtime exists yet);
- the Claude Agent provider and shell-permission changes at runtime
(source only);
- macOS/Linux.

---

## Design companion finding

**Symptom:** `scripts/test-design-companion-native.mjs <cli>` passes on
2.2.1 and fails on 2.2.2 and 2.2.3 with `Error: Timed out: series 1
generated and read`. Reproduced independently in a scratch checkout of
`origin/dev` (`f39642bc4`), with `CODENOMAD_NATIVE_FIXTURE_PARENT`
outside the home:

```
test-design-companion-native 2.2.1  exit=0  PASS AC-16, AC-52
test-design-companion-native 2.2.2  exit=1  Timed out: series 1 generated and read
test-design-companion-native 2.2.3  exit=1  Timed out: series 1 generated and read
```

**What the step waits for**
(`test-design-companion-native.mjs:238-241`): the fixture's loopback
chat provider answers a `RUN [...]` turn with two tool calls `{ tool:
"image_generate", args: { prompt, path: ".codenomad/design/icon/<name>"
} }`. The step then waits until the transcript holds two synthetic
notices with `metadata.source === "image_generate"` (line 209) and two
`read` parts of the generated files. No HTTP generate route, file watch
or media blob is involved; it is the agent tool path.

**Cause: an intentional upstream contract change, not an upstream bug.**
In 2.2.2, [#60258](anomalyco/opencode#60258)
(`d4e48f54da`, "turn experimental_image_generate on by default") changed
`packages/core/src/tool/plugin/image-generate.ts`:

- `export const name = "image_generate"` →
`"experimental_image_generate"`. That one constant is the tool name, the
permission action (`assertions: ["experimental_image_generate", "edit",
"read"]`), the Job `type`, and the completion notice's
`metadata.source`. `session/execution/restart.ts` also tags
restart-recovery notices `source: "experimental_image_generate"`.
- The opt-in became default-on: `if (!enabled || imageGenerate(entries)
=== false) return`. An unset `experimental.image_generate` now offers
the tool; only `false` removes it ("still honored for configs from when
the tool was opt-in; permissions are the supported way to turn it off").
- The 2.2.2 changelog states it: "agents can now use
`experimental_image_generate` without additional configuration".

2.2.2 also wraps the notice text as `<image id="…"
state="…">\n…\n</image>` and adds `id` to the tool output
([#60098](anomalyco/opencode#60098)).
CodeNomad's `Saved image: (.+) \(` parse still matches; `jobID`, `state`
and `path` metadata are unchanged.

**Evidence** (fixture DBs copied read-only to `evidence/`):

```
2.2.3, original fixture (calls "image_generate"):
  seq 5  tool image_generate  error  "No tool named \"image_generate\" is currently available. Please use a tool from the available tool list."  (x2)
  -> no image job, no notice, no read: the wait times out.

2.2.3, fixture with the tool name changed (scratch copy; CodeNomad server code unmodified):
  seq 5  tool experimental_image_generate completed {path:…/icon-minimal-a, model:"fiximg/image", jobID:"image_18cfb59…"}
  seq 18 synthetic {"source":"experimental_image_generate","jobID":"image_18cfb59…","state":"completed",…}
         <image id="image_18cfb59…" state="completed"> | Image request finished. Saved image: …
  seq 20 tool read completed (x2) … seq 40/48/49 refinement
  -> files, image calls and reads all correct, then
     AssertionError: generations.length 0 !== 3   (test line 260: gallery "generation" entries)
```

So the product bug is real and independent of the fixture: on 2.2.2+
CodeNomad's Images gallery finds **no generation entries**. Images still
appear through their `read` parts, but without prompt, model, lineage or
"proven" origin, and `galleryImageReference` refuses them as `changed`.

**CodeNomad code that assumes the 2.2.1 name or default** (origin/dev):

| Where | Assumption | Effect on 2.2.2+ |
| --- | --- | --- |
| `session-pruning/assets-gallery.ts:56,139` | `part.name ===
"image_generate"` | no generation entries; references refused `changed`
|
| `session-pruning/assets-gallery-proof.ts:104,130` | tool name and
notice `source === "image_generate"` | no calls or notices, so no
provenance proof |
| `opencode/image-settings.ts` `imageGenerationSelection(undefined) →
false` (doc: "Reads … as OpenCode does") | unset means off | Settings →
Images shows **Off** and `images.status()` returns `"disabled"` while
the tool is offered; help step "Turn on image generation"
(`docs/help/en/extensions.md:73`) is no longer needed |
| `opencode/design/opencode/agents/design.md`, `skills/design-core`,
`design-logo`, `design-ui-mockup` | the agent is told to call
`image_generate` | the model sees only `experimental_image_generate`;
the first call can fail with "No tool named …" |
| `scripts/test-design-companion-native.mjs:125,209,238,301` | fixture
calls/filters `image_generate` | red on 2.2.2+ |
| `scripts/test-design-activation-native.mjs:76,307` | ask rule on
action `image_generate`; tool detection by that name | **passes but
loses coverage**: `askTool` falls back to `webfetch` on 2.2.2/2.2.3
(logged) |
| `dev-docs/SESSION_PRUNING_RPC.md`, `PANEL_EXTENSIONS.md`,
`SETTINGS.md` | describe `image_generate` parts and opt-in | out of date
for 2.2.2+ |

User permission rules written for action `image_generate` no longer
match on 2.2.2+; that is an OpenCode-side change for users. CodeNomad's
help never names the permission action; the Design fixtures and agent
use `experimental_image_generate`.

**Fix in this PR** (2.2.3 contract only, no fallback):

1. One `IMAGE_TOOL = "experimental_image_generate"` constant drives the
gallery and its proof; the gallery reads the 2.2.3 notice format.
2. The native fixtures use the new tool; the activation fixture
exercises it (default on) instead of falling back to webfetch.
3. The Design agent and skills name `experimental_image_generate`.
4. `imageGenerationSelection` reads an unset
`experimental.image_generate` as on; only `false` disables it. Settings
→ Images and `images.status()` report it that way.

## Follow-up

- Hardening for `session_part` (independent of this upgrade, no 2.2.3
regression): make the direct readers and the pruning transaction fail
safe when `session_part` has rows for the target messages.

## Isolated Windows qualification

Base: dev `f39642bc4`; Node 25.2.1 (local; CI runs the pinned 24.20.0);
exact official CLI **2.2.3**; locked client/plugin **2.1.0**;
`CODENOMAD_NATIVE_FIXTURE_PARENT` outside the user profile for the
Design fixtures.

- **Before the image fix (recommendation-only change):** every native
suite passed on 2.2.3 except the Design companion end to end, which
passes on 2.2.1 and times out on 2.2.2/2.2.3 at "series 1 generated and
read" (`No tool named "image_generate" is currently available`).
- **After this change, on 2.2.3:** Design companion (3 files, 2 series,
all proven, lineage), Design activation (image tool offered to Design
and Build, default on), Design content, image settings (default on,
Off→On hot reload), session pruning including session images/media, Code
Mode jobs `{ total: 2000, beforeAwait: 1000, count: 1000 }`.
- **Full native set on 2.2.3 (before the image fix, unaffected areas):**
plugin runtime, automation, Location/worktrees/event relay, permission
receipts, Git init/degraded, Missions (contract, family control, native,
trajectory) and all 11 recurring journeys, sessions (environment, fork,
aside, interrupt, lifecycle, blank cleanup, compaction, prompt skills,
providers, service usage, web search, plugin packages, output limit,
Promise-tool failure, subagent depth, temporary workspaces), migrations
from 2.1.2/2.2.1/2.2.2, install/upgrade/legacy replacement.
- **Units and checks after the change:** root and server typecheck
clean; server **2,825 passed, 9 skipped, 1 cancelled** (`index.test.ts`
shutdown test timed out under full-suite load; **4/4** alone); gallery
107/107; Design 23/23; opencode-update 62 passed / 1 skipped; touched UI
units 8/8; browser `opencode-setup` + `websearch-settings` 38/38; native
plugin build 2/2; ten-locale help 31/31.
…er the same name (#1210)

A failed image generation (here three Gemini calls rejected with 'API
key not valid') followed by a retry under the same file names made every
retried image 'origin not verifiable' in the Design light table, and the
retry's generation showed 'No read-back image attached': the proof saw
two possible writers of each file.

OpenCode 2.2.3 publishes a job's notice only after `jobs.wait`
(`image-generate.ts` `notifyWhenDone`), and a failed or cancelled job
saves nothing afterwards. `proveWriter` now leaves out a call whose
single decoded notice (per `generationState`) reports `error` or
`cancelled` before the remaining writer's start. Notified after the
retry started, never notified, or notified twice → still unverifiable.

- `assets-gallery-proof.ts` (+10/−1), `assets-gallery-bounds.test.ts` (5
cases × plain/blob; positives red without the change),
`dev-docs/SESSION_PRUNING_RPC.md`.
- Checks: session-pruning, session-pruning route and API 4 tests; server
typecheck.
Help tables broke words in the middle of narrow columns ("Compone nt", "CodeNom ad project tab" in Missions → Lifetime and service restarts). Cells used `overflow-wrap: anywhere`, which also lowers each cell's minimum width to one character, so the automatic table layout squeezed short label columns in favor of long description columns.

Cells now use `overflow-wrap: break-word`: an over-long word still wraps, but column widths are computed from whole words. Inline code keeps `overflow-wrap: anywhere`, and wide tables keep scrolling locally.

test-help-browser.mjs checks, in every English article at 390 and 1440 px, that no word outside code spans two lines in a table cell; it fails with the previous rule and the help browser suite passes (47/47). Review checked all ten locales at both widths.
… login (#1214)

Usage showed nothing for OpenCode's `claude-agent` provider (Claude Agent SDK), which signs in with Claude Code's own login (`claude auth login`): CodeNomad had no usage reader for that ID.

`claude-agent` is now an alias of the existing `claude-code` reader, which reads Claude Code's login on the daemon host; both IDs share one cached reading and backoff, and the login never appears in responses or logs. Without a Claude Code login it shows not configured, with no fallback to another provider's credential. PROVIDER_ACCOUNTS.md, the server README and the providers help page (ten locales) are updated.

Validation: service and route tests cover both IDs and fail without the alias; usage, route and credential tests (29/29), root typecheck and the help checks pass.
)

The Mission editor's Playbook selector showed only names; what each playbook does was documented only in the help. The selected playbook's one-sentence description now appears directly under the selector (referenced by its aria-describedby) and follows the selection.

Five `missions.control.template.<id>.description` strings reuse each locale's help wording in all ten locales; the paragraph takes the editor's existing secondary text style. The Missions help (ten locales) and dev-docs/MISSIONS.md mention it.

Validation: a browser test opens the real editor, checks the default description, its placement and fit, then Exploration and Compare two models; it fails without the component change. A unit test checks every locale has its own description for each playbook. Mission editor and automation browser tests (98/98), UI unit tests, help checks and the root typecheck pass.
A user asked whether sessions and sub-agents in a mission can use earlier sub-agent sessions and mission context. The Missions help now explains how context flows: the mission map as shared, durable memory; what the coordinator, native-subagent tasks, independent-session tasks and their helpers each receive; how results come back; how later tasks build on earlier ones (dependencies, reused conversations, existing independent conversations) and what compaction means for model context versus stored history; daily runs and follow-up missions; and on-demand search of earlier conversations through CodeNomad's session search tool.

The section (`#context-flow`, `#context-search`) is in all ten locales with each locale's UI labels; every statement is mapped to the code in the PR description.

Validation: help structure and browser checks pass (31/31, 47/47); two review rounds, the second with no findings.
…t now (#1219)

A user reported that pausing a mission stopped it abruptly, mid-step; pausing by hand ("pause this session" queued to the coordinator and sub-agent) let the current step finish and resumed cleanly. Pause interrupted the whole native family at once.

Pause from the panel is now graceful (`graceful: true`): it records a paused control with no targets and no native side effect, so it settles at once. The paused context every actor reads at each model request asks it to finish only the step in progress, start no new work or sub-agents and end its turn; the plugin's context hook adds it for native descendants, walking ordinary sub-agents' ancestry (bounded, cached parent IDs) to their actor and re-reading a paused chain so only sessions still placed with the actor's admitted project and Location receive it. Dispatch blocking and report parking apply at once, as before.

While the activity projection observes a running, queued, background, Form or permission actor of a gracefully paused mission, the panel shows "Pausing: the steps in progress are finishing." and Interrupt now, which sends the existing immediate Pause (family interruption and receipts unchanged). An immediate Pause is valid from paused only after a graceful one. Play after a graceful Pause tells the coordinator that delegated work may have stopped early and should be continued, not restarted. The flag is carried by the route, RPC schema, plugin, journal (graceful means no targets), projection and UI intents; requests without it keep the immediate behavior.

MISSIONS.md, the Missions help (ten locales) and three UI strings (ten locales) are updated. Tests cover the lifecycle transitions and replay, the journal rule, the real plugin context hook for the coordinator and three levels of sub-agents (including moved sessions and warm caches), and the real panel for running and Form-waiting work. Three review rounds; the last had no findings.
…th their names (#1218)

The Model selectors of mission profiles (Settings → Missions defaults, Mission editor Options, Compare Model A/B) listed every catalog model as raw `provider/id`, unfiltered. They now show the same list and names as the conversation's model selector.

The composer's catalog building moves unchanged into `lib/model-catalog.ts` (`buildProviderCatalog`, `listCatalogModels`), and preference filtering into `stores/model-picker.ts` (`preferredModelList`): hidden models are left out, favorites-only applies while a favorite remains, and models sort by provider then name. Mission controls reuse them, keep only tool-capable models, and group options by provider. Summaries show friendly names without their own catalog read (`knownModelName`), with names keyed by the workspace-root Location and connection generation so a fresher conversation catalog wins. A saved value the list leaves out stays selected under its name, or as "· Unavailable" when the selector cannot offer it; stored values keep the `providerID/id` format. A global `optgroup` rule styles provider groups in native selects.

Help (ten locales) and MISSIONS.md describe the list. Tests cover the shared helpers, freshness and Location fences, and the real Settings, editor and Compare selectors, including hidden, favorite-filtered, unknown and tool-less saved values; three review rounds, the last with no findings. Rebased on #1219: typecheck, help checks and 77 mission/model browser tests pass.
Setting many rows of the same kind (Message content eyes, session
selection checkboxes, model visibility) took one click per row. Image
editors let users drag across a column of layer eyes instead; CodeNomad
now does the same.

Pressing a toggle and dragging out of it flips it, and every toggle of
the same column or list the pointer then crosses takes that new state;
toggles already in that state stay as they are. Releasing the button
clicks nothing, including the first toggle when the drag returns to it.
It works for mouse and pen; touch drags keep scrolling, and a touch
landing during a mouse drag leaves the sweep going.

Implementation: `lib/toggle-sweep.ts` installs one capture listener on
first use. Toggles opt in with `data-toggle-sweep={toggleSweepGroup(...)}`,
one group per column or list, so the Message content eyes and chevrons
sweep separately and exclusive selectors (tabs, presets) never sweep.
Each toggle changes through its own click, so its handlers, disabled
state and accessibility are unchanged; state is read from `checked`
(labels stand for their checkbox) or `aria-pressed`/`aria-checked`. The
drag is sampled every 4 px so fast moves skip no row, text selection
and native drag are blocked during a sweep, the trailing click is
swallowed, and a sweep whose release went elsewhere ends at the next
move without the button. `afterSweep` defers costly work until the
sweep ends.

Groups: Message content visibility and expansion columns, session
selection checkboxes, right-panel customization (built-in tabs and
Status sections; extension activation is a trust decision and stays
out), notification routing, model visibility, and the multi-select
options of agent forms and provider sign-in forms.

Message content dropped clicks made while a save was pending and showed
a row only once the server confirmed it, re-rendering the transcript
for each. Rows now show their requested state at once; a sweep saves
once, on release, so the transcript re-renders once. Saves run in
order and each batch of rows is sealed as it joins the queue, so a
later click never jumps ahead of a preset change nor rides along with a
sweep still held.

Notification routing shows each box at once, writes the whole table
once per sweep, runs its writes one after another so an older table
never lands last, and asks for System permission once per gesture.
Model visibility already showed pending changes and wrote them in
order.

Validation: browser regressions in transcript-presets (a single jump
across four eyes with slow saves, shown during the drag with nothing
saved, then one write on release; a hidden row that stays hidden; a
release back on the origin; the chevron column alone; a held sweep not
persisted by queued saves; a row clicked after a preset applying after
it), session-search (both directions, a release over a row title
opening no session, a lost release leaving no sweep, a touch mid-drag)
and notification-routing (one write per sweep, serialized writes with
the last winning, one permission request). Each fails with its part of
the fix removed. Two gatekeeper rounds ended with no P0-P2 finding; the
user validated the behaviour in a separate Electron dev instance.
dev-docs/UI_CONVENTIONS.md and dev-docs/TRANSCRIPT.md record the
convention, and the help describes the gesture in all ten languages.
Follow-up to #1183, which merged with the sticky creation footer. That
footer's second review left two P2s open:
- **R3:** a stale-map or cleanup-unavailable banner rendered after the
form moved Create by about 32 px during a native automation click.
- **R4:** on short panels, the sticky footer covered the focused field
or made Options unreachable.

## Change
- **Layout:** while creating, the form fills the Missions panel as a
column. Its **body is the only scroller**, and the footer sits below it,
outside that scrollport, so native focus scrolling keeps the focused
field visible.
- **Banners** shown during creation are ordered above the form. They
shrink the body and never move the footer; their text and roles are
unchanged.
- **Short panels** (the footer plus an 8rem body don't fit): the whole
form scrolls in the panel, with the footer after it. This uses the
existing short-layout helper, renamed `mission-panel-scroll.ts` and
shared with the task tree, which re-reveals the focused element when the
mode switches.
- **Unaffected:** edit/delete forms and the Preferences depth editor.

## Validation
- **automation-forms:** five held outcomes (three depth reads, a stale
banner, cleanup unavailable) released during the real controller's
click. Create's box is unchanged and exactly one POST is sent.
- **mission-panel-host:**
- A real-keyboard Tab pass at 1000x600 and 260x300, with Options and the
brief prompt open, hit-tests every focused control.
  - At 220x120, Options opens and every control is reachable.
- **Red checks:** the sticky CSS fails both banner cases (-32.5 px) and
the focus checks; removing the fallback fails the short-panel checks.
- **Passing on current dev:** UI typecheck; automation-forms (2 runs),
mission-panel-host, mission-control, mission-panel-layout,
mission-panel-geometry, mission-create-submit, mission-create-refusals,
mission-editor-lifetime.
notification-routing.test.ts (#1221) serves the status-tokens fixture through startProductFixture like its sibling tests but was missing from the fixture exemption map, so fixture-wiring.test.ts failed browser shard 1. It now carries the same exemption. fixture-wiring.test.ts and notification-routing.test.ts pass locally (3/3).
## Summary

- OpenCode **2.2.5** becomes the **recommended** (qualified) runtime.
The **2.2.3 minimum** and the 2.1.0 `@opencode/client`/`plugin`/`schema`
pins stay: neither 2.2.4 nor 2.2.5 changes a contract CodeNomad
consumes.
- `RECOMMENDED_OPENCODE_VERSION` is 2.2.5, and the reviewed-publication
shortcut in `contractProfile` lists 2.2.3 and 2.2.5; other versions,
2.2.4 included, keep negotiating their authenticated schema. The CI
`latest` lane already resolves the latest stable CLI.
- Help (all ten locales), READMEs, `CONTRIBUTING.md`,
`dev-docs/OPENCODE_COMPATIBILITY.md` and the architecture skill state
two separate versions again: minimum 2.2.3 with its reason,
recommendation 2.2.5.

## Contract review (v2.2.3 → v2.2.4 → v2.2.5; 31 + 19 commits)

Reviewed from the exact tags `v2.2.3` (`082ac6281f`), `v2.2.4`
(`45898d0f85`) and `v2.2.5` (`9bbb8ce18c`), the published npm packages,
and an authenticated OpenAPI dump from each official CLI running in its
own sandbox. The [changelog](https://opencode.ai/changelog) has full
notes for 2.2.4 and 2.2.5 (both 30 October); everything below was also
checked against the commit log and source. CodeNomad side: `origin/dev`
`605820669`.

**Headline:** neither release changes a contract CodeNomad consumes.
2.2.4 adds opt-in **artifacts** (seven `/api/experimental/artifacts`
operations, four tools, a plugin hook), **renamable persistent
terminals**, and moves background-job recovery markers from `kv` into a
new **`job` table**. 2.2.5 is a bug-fix release; its one schema change
is that `compaction.buffer` accepts a percentage such as `"20%"`. The
image tool contract adopted in #1209 (`experimental_image_generate`, on
by default) is unchanged through 2.2.5, and still nothing writes
`session_part`.

- **Authenticated OpenAPI:** 134 → **138** → 138 paths, 162 → **169** →
169 operations, schemas 351 → 362 → 363.
- **Added operations (2.2.4):** `GET
/api/experimental/artifacts/providers`, `GET|POST
/api/experimental/artifacts`, `GET|PATCH|DELETE
/api/experimental/artifacts/{artifactID}`, `GET
/api/experimental/artifacts/{artifactID}/view/*`
([#60351](anomalyco/opencode#60351)). None
removed in either release.
- **Changed operations (2.2.4):** `GET /api/event`, where the `types`
filter enum gains `persistent-pty.updated` and `artifact.updated` (94 →
96 types, none removed). `PUT /api/experimental/persistent-pty/{ptyID}`
can also rename (`title`; `null` clears it), `size` becomes optional,
and a 501 `PtyFeatureUnsupportedError` is returned while an older
terminal service can't rename
([#60329](anomalyco/opencode#60329)).
  - **Changed operations (2.2.5):** none.
- **Consumed schemas:** same family selector as the 2.2.3 review, 97
schemas at every version.
- 2.2.3 → 2.2.4: 95 byte-identical. The two that change,
`PersistentPty.Info` (+ `userTitle`, nullable, required) and
`PersistentPty.UpdateInput` (`size` optional, + `title`), are both
additive.
    - 2.2.4 → 2.2.5: all 97 byte-identical.
- `V2EventEncoded`, `Session.*`, `Form.*`, `Permission.*`, `Prompt*`,
`Event.*`, `Location.*`, PTY/Shell are byte-identical from 2.2.3 to
2.2.5.
- **`Config.InfoEncoded`:** 2.2.4 adds `experimental.artifacts`
(boolean, opt-in). 2.2.5 turns `compaction.buffer` into
`Config.Compaction.Percent | integer`
([#60647](anomalyco/opencode#60647)).
`experimental.image_generate`, `experimental.media_blob` and
`experimental.schedules` are unchanged.
- **Admission:** every predicate in `compatibility/negotiate.ts`
(unchanged on origin/dev) holds on 2.2.3, 2.2.4 and 2.2.5.
`contractProfile` lists only `2.2.3` as reviewed, so 2.2.4/2.2.5 are
admitted through authenticated contract recognition.
- **Instance proxy:** the allowlist is unchanged on origin/dev. No added
or changed route is proxied: artifacts and persistent PTYs are outside
it. The proxied `GET /api/config` returns the additive config fields
above.
- **Published declarations** (2.2.3 → 2.2.5):
- **Client:** 185 → **192** → 192 methods. 2.2.4 adds
`experimental.artifacts.{providers,list,create,get,update,delete,view}`,
the artifact and `PersistentPtyUpdated` types, and
`PtyFeatureUnsupportedError`. It removes
`createPtyClient`/`PtyConnectInput` from `@opencode/client/solid/pty`,
which CodeNomad doesn't import. 2.2.5 adds `ConfigCompactionPercent`.
- **Schema:** 2.2.4 adds the `artifact` module and
`PersistentPty.Updated`; 2.2.5 adds `config/compaction` `Percent`.
- **Plugin:** 2.2.4 adds `effect/artifact` and `promise/artifact` and
`ctx.experimental.artifact` (`transform`, `reload`). 2.2.5 has no export
changes.
- **Typecheck:** the server and UI typecheck with **0 errors** against
the 2.2.4 and 2.2.5 client/schema/plugin/effect declarations. The pinned
2.1.0 control is also clean.
- **Host modules:** the bundles' host imports are identical to the 2.2.3
review. A probe plugin in an isolated **2.2.4** and **2.2.5** daemon
resolves every module and binding. `runtime-modules.ts` is unchanged
from v2.2.3 to v2.2.5.
- **Host Effect:** stays **4.0.6** (`bun.lock` sha512 identical;
published `dependencies.effect` 4.0.6).
- **Persistent PTY binary:** the 2.2.4 and 2.2.5 CLIs ship
`@opencode/pty` **0.4.0** (string `opencode-pty 0.4.0` in the Windows
binary; 2.2.3: `0.3.3`). `process-daemon-v0.4.0` adds only terminal
renaming ([#60324](anomalyco/opencode#60324)):
an additive `rename` request and `features: ["rename"]` with an
unchanged `PROTOCOL_VERSION`. CodeNomad only lists persistent PTYs
(worktree deletion guard) and reads `status`/`cwd`.
- **Private native services:** the keys (`LocationServiceMap`, `Form`,
`Permission`, `Location`, `Job`, `Database`, `Session`, `Bus`, `Shell`)
and the members CodeNomad calls are unchanged. The changes are additive:
`Job.cancelSession` (2.2.4), `Permission.allows` (2.2.5; never asks,
used by `edit` to decide whether to quote file lines), and
`Session.remove` now calls `jobs.cancelSession`. The Missions recurrence
clock calls `Job.get/start/cancel`. Its Job metadata carries no
`sessionID`, so deleting a session doesn't cancel it.
- **Storage:** one migration, `20261030054400_job` (2.2.4,
[#60491](anomalyco/opencode#60491)). It creates
`job`, copies every `kv` row `job.background/%` into it and deletes
those `kv` rows. 2.2.5 adds none.
- CodeNomad reads only its own `kv` keys (Missions, pruning receipts and
claims), never `job.background/*`.
- Native migration 2.2.3 → 2.2.4 passes. Every 2.2.4/2.2.5 fixture DB
inspected holds the `job` table, **0** `job.background/` rows and no
leftover `schedule:leader` lease.
- **`session_part`:** still **not written**. `history.ts`/`projector.ts`
are unchanged from v2.2.3 to v2.2.5, the projector still only rewrites
existing part rows, and every inspected 2.2.4/2.2.5 fixture DB has **0**
rows. The forward risk from the 2.2.3 review stands (Recommendations).
- **Behavior changes checked** (plain-language list in [What users will
notice](#what-users-will-notice)):
- **Image tool** (#1209 contract): `image-generate.ts` is unchanged from
v2.2.3 to v2.2.5. 2.2.4 refactors restart recovery into one
`restartMessage` but keeps `metadata.source:
"experimental_image_generate"`, `jobID`, `state`, `path` and the `<image
id state>` notice text. Design companion, activation, content and image
settings pass natively on 2.2.4.
- **Schedules** (2.2.4,
[#60478](anomalyco/opencode#60478), then
`da87c475a7`): the loop now runs in every server process, including
`opencode serve`, and elects a leader through a `kv` lease
`schedule:leader` (claimed at boot, released when there are no active
schedules). Firing stays opt-in (`experimental.schedules === true`).
CodeNomad uses neither OpenCode schedules nor that key; Missions
recurrence uses its own storage and the Job service.
- **Sessions:** deleting a session now cancels its running background
jobs (shell, image, subagent) and drops their markers (2.2.4). That is
the intended upstream cleanup; CodeNomad's session deletions
(blank-session cleanup, temporary workspaces) get it automatically.
- **Events:** `persistent-pty.updated` and `artifact.updated` reach
CodeNomad's shared event relay. The relay routes generically
(`instance-events.ts`), the 2.1.0 client's SSE reader doesn't validate
types, and the UI handles neither type. The native relay suite passes on
2.2.4.
- **Tools** (2.2.5): a model call to `bash` runs as `shell` with shell
permissions, and the result carries a note
([#60687](anomalyco/opencode#60687)). CodeNomad
already groups and renders `bash` and `shell` alike
(`message-part-grouping.ts:48`, `tool-call/utils.ts`). `read`/`edit`
failure texts are richer
([#60688](anomalyco/opencode#60688),
[#60689](anomalyco/opencode#60689)).
- **Compaction** (2.2.5): the prompt is capped at the context window
when a catalog's input limit is larger
([#60660](anomalyco/opencode#60660)), and
`compaction.buffer` may be `"N%"`. CodeNomad never reads or writes
`compaction.buffer`. LiteLLM context errors now trigger compaction
([#60692](anomalyco/opencode#60692)).
- **Reasoning replay** (2.2.5,
[#60498](anomalyco/opencode#60498)): reasoning
is no longer re-sent as plain text after a model switch, and interrupted
or errored reasoning is dropped from the request. This changes provider
requests only; stored transcripts and `Session.Message.*` are
byte-identical.
- **MCP** (2.2.5): stdio messages over 10 MiB no longer disconnect a
server ([#60694](anomalyco/opencode#60694)), and
an idle Location no longer restarts its MCP servers right after shutting
down ([#60668](anomalyco/opencode#60668)).
- **Providers:** a Console-supplied provider can now be overridden by
user, project and remote config (2.2.4). The Claude Agent provider only
replaces Claude Code's `/login` hint with `claude auth login` in its
errors ([#60592](anomalyco/opencode#60592)); the
guided setup
([#60603](anomalyco/opencode#60603)) is OpenCode
Desktop only. The provider ID (`Provider.ID.claudeAgent`) and its use of
Claude Code's own login are unchanged, so CodeNomad's `claude-agent`
quota reader (#1214) is unaffected.
- **Service lifecycle:** the CLI now adds `127.0.0.1,localhost,::1` to
`NO_PROXY`/`no_proxy` for itself and its children
([#60441](anomalyco/opencode#60441)), so
loopback traffic from daemon-hosted code, CodeNomad's plugins included,
bypasses environment proxies. CodeNomad sets no proxy variables.
- **Code Mode, Forms, permission prompts, media blobs:** no source
changes from v2.2.3 to v2.2.5 (`packages/codemode` only bumps its
version). The Code Mode run-to-completion regression passes on 2.2.4: `{
total: 2000, beforeAwait: 1000, count: 1000 }`.
- **`@opencode/ai`:** one `ai` object for Promise and Effect
([#60516](anomalyco/opencode#60516); left out of
the changelog by
[#60631](anomalyco/opencode#60631)). CodeNomad
doesn't import `@opencode/ai`.
- Desktop/TUI-only changes (desktop terminals on persistent PTYs,
base-branch and worktree shortcuts, quote sending, flat inputs, palette
ordering) don't affect CodeNomad. One indirect effect follows.
- **Worktree deletion:** OpenCode Desktop terminals now live as
persistent PTYs that survive restarts
([#60346](anomalyco/opencode#60346)).
CodeNomad's guard (`routes/worktrees.ts`, "Running persistent PTY blocks
deletion: <id>") will therefore refuse more often for users who also
open terminals in OpenCode Desktop. The behavior is correct, but the
message names the PTY ID rather than the terminal's title.

**Verdict:**

- The **2.1.0 client/plugin pins can stay**. Bumping them would bring
only artifacts, PTY renaming and a config type, none of which CodeNomad
consumes.
- The **2.2.3 minimum has no reason to move**. No consumed HTTP, plugin,
private-service or storage contract broke in 2.2.4 or 2.2.5.
- **Recommend 2.2.5.** It is admitted today through contract
recognition. Recommending it is CodeNomad-side bookkeeping:
`RECOMMENDED_OPENCODE_VERSION` (`runtime-support.ts`), the
reviewed-version check in `contractProfile`
(`compatibility/runtime.ts`), the version texts in docs and help, and
CI's qualification target. This is subject to the full native suite on
the exact 2.2.5 CLI, run separately.
- **No CodeNomad bug found** in 2.2.3 → 2.2.5. Risks: the `session_part`
forward risk (unchanged) and the more frequent worktree-deletion refusal
(UX).

**Not verified here:**
- the full native suite on 2.2.5: only a host-module probe ran natively
on 2.2.5, and everything else for 2.2.5 is source and OpenAPI review
(Appendix I);
- the 2.2.4 recurring Missions journey (`test-recurring-simple-native`
ABCDGELFWQN), which was interrupted by a server restart;
- the `kv` → `job` migration with a real pending background marker: only
the empty case ran, and upstream `job.test.ts` covers the copy;
- whether a `bash` call is stored as `bash` or `shell` (source suggests
the model's name is kept; CodeNomad renders both);
- artifacts, Claude Agent and MCP stdio framing at runtime (source
only);
- macOS/Linux.

---

## What users will notice

**OpenCode 2.2.4**
- **Persistent desktop terminals:** OpenCode Desktop terminals belong to
the session, keep their scrollback across server restarts, stay in sync
with the TUI and other windows, and can be renamed. Renamed terminals
keep their names.
- **Artifacts (opt-in):** with `"experimental": { "artifacts": true }`,
agents can publish, update and serve HTML or static artifacts
(`artifact_create`, `artifact_read`, `artifact_update`,
`artifact_delete`) through a built-in local provider, which stores files
under OpenCode's data folder, or a plugin-provided one.
- **Schedules:** enabled schedules also fire under `opencode serve`, and
only once when several OpenCode processes share a database. They stay
off unless `experimental.schedules` is `true`.
- **Claude Agent:** OpenCode Desktop guides installation, `claude auth
login` and connection checking. Error messages now say `claude auth
login` instead of `/login`.
- **Config:** user, project and remote configuration can override
Console-supplied provider names, limits, variants, settings and headers.
- **Networking:** requests to the local OpenCode service no longer go
through `HTTP(S)_PROXY`.
- **Sessions:** deleting a session also stops its running background
work, such as shells, image generations and subagents.
- **Desktop/TUI:** base-branch shortcut, simpler location popover,
faster Markdown streaming and various fixes.

**OpenCode 2.2.5**
- **Compaction:** `"compaction": { "buffer": "20%" }` keeps a share of
each model's context free; token counts still work. Compaction no longer
overfills models whose catalog input limit exceeds their context window,
and LiteLLM context errors trigger compaction instead of stopping.
- **Tools:** a model calling `bash` gets `shell` (with shell
permissions) instead of an error. `read` names the missing folder, and
`edit` shows the closest lines when `oldString` isn't found.
- **Reasoning:** switching models or resuming an interrupted reply no
longer resends reasoning as plain text, which some providers rejected.
- **MCP:** large stdio messages (over 10 MiB) no longer disconnect a
server, and idle locations don't immediately restart their MCP servers.
- **Desktop/TUI:** worktree and base-branch shortcuts, a shell-mode
toggle, sending a single quote from its popup, and MCP servers removed
from the connect menu.

## Follow-ups (outside this PR)

- Hardening for `session_part` (independent of this upgrade; 2.2.5 still
writes no rows): make the direct `session_message.data.content` readers
and the pruning transaction fail safe when `session_part` has rows for
the target messages.
- Optional UX: when the worktree deletion guard finds a running
persistent PTY, name it by `userTitle ?? title` (2.2.4+) and say it may
be an OpenCode Desktop terminal.
- Any future reader of `compaction.buffer` must accept `"N%"` as well as
a token count.

## Isolated Windows qualification

Base: dev `605820669`; Node 25.2.1 (local; CI runs the pinned 24.20.0);
exact official CLI **2.2.5**; locked client/plugin **2.1.0**;
`CODENOMAD_NATIVE_FIXTURE_PARENT` outside the user profile.

- **Native suites on 2.2.5, all passing:** Code Mode jobs, plugin
runtime, automation, session pruning (history, navigation, media blobs,
proxy, session images), Location/worktrees/event relay, permission
receipts, Git init/degraded, Missions (contract, family control, native,
trajectory) and all 11 recurring journeys, sessions (environment, fork,
aside, interrupt, lifecycle, blank cleanup, compaction isolation, prompt
skills), providers (usage, accounts, account selection), service usage,
web search settings, plugin packages, output limit, Promise-tool
failure, subagent depth, temporary workspaces, Design
companion/activation/content, image settings, history migrations from
2.1.2/2.2.3/2.2.4, shared install of the 2.2.3 minimum.
- **Installer suites after the bump:** upgrade (installed `opencode
v2.2.5`) and legacy replacement (target `2.2.5`) install the new
recommendation and pass.
- **Builds and checks after the change:** server build, native plugin
builds and root typecheck clean; server **2,840 passed, 9 skipped, 1
cancelled** (`index.test.ts` shutdown test timed out under full-suite
load; **4/4** alone); runtime-support/transport/update **35/35**;
browser `opencode-setup` **34/34**; ten-locale help **31/31**.
…ist read (#1223)

A mission created right after the Missions panel opened could stay on a
disabled "Saving…" even though the creation request succeeded. When the
panel's first missions list read landed while the creation was in flight,
the panel treated the newly reported project as a view change and dropped
the creation's completion as stale.

The fence in lib/mission-view-fence.ts (createReportedChanges, used by
MissionControl.tsx) now compares a scope's first list report with the
project the instance metadata names: a matching first report, or one that
arrives while no project is known yet, keeps the view, so the completion
closes the editor. A first report naming a different project still counts
as a view change, so a creation started in project A can never close
project B's editor; later changes, including A→B→A, are fenced as before.
The project passed to the editor and its creation hold are unchanged.
dev-docs/MISSIONS.md states the rule.

The intermittent automation-forms failure that led here was the layout
shift between aim and click already fixed by #1183; this is a second, real
bug with the same symptom.

Tests: two browser regressions in mission-create-submit.test.ts with the
real components (a first list read held until the creation request is in
flight, and a first read reporting another project), each failing without
its fix; unit tests in mission-view-fence.test.ts for matching,
contradicting and per-scope first reports. The mission editor, creation
and panel browser suites (185 tests), the automation-forms file in
repeated loops and the UI typecheck pass.
…ord who decided (#1227)

Missions asked the human too many questions, too early, and in Exploration
the questions came straight from sub-agents: since the decision worker had
to ask the human from its own conversation, the coordinator could neither
answer, defer nor batch them. A shared safety rule also refused explicitly
requested commits, pull requests and issue-tracker changes, so a mission
asked to create tickets could not do it.

Only the coordinator now talks to the human, in every playbook. Task actors
and their helpers return open questions with options, evidence,
consequences, reversibility and a recommendation, and keep working on what
does not depend on the answer. Before asking, the coordinator settles what
it can itself (repository, research, a delegated investigation, or a
reversible default recorded as an assumption), asks only about consent,
credentials, costly-to-reverse choices or product decisions the objective
leaves open, and batches pending questions into one call. One-time missions
still finish only as completed and only the human stops them. Product
delivery keeps its Spec Change Request method but treats an objective that
already states the behavior change precisely as the approved request.

Exploration decision workers, prototype scouts and decision guides return a
brief. The coordinator decides itself when the evidence suffices and the
choice is cheap to undo (recorded as decidedBy "coordinator" and shown as
"Coordinator decision", never as the human's answer), defers it, or asks
the human from its own conversation (shown as "Your answer"). A claimed
human answer still needs a confirmed UI mark. Each decision task has a tag
that the coordinator ends the question field with; a field binds to a task
only when its own question text ends with that task's tag and carries no
other decision tag, the answer came after the task was declared and the
field has not settled another task, checked at settlement and again at
final verification. Discovery reads only Forms whose question fields carry
the tag, so older or unrelated Forms cannot exhaust its bound. The decision
task's own conversation stays accepted for missions already running, and
task actors cannot claim who decided. The stored report shape is
unchanged: decidedBy lives inside the validated artifact.

The safety boundary keeps staging, committing, pushing, pull requests and
issue-tracker changes forbidden by default, except when the mission
objective or notes explicitly ask for that specific action; only that
action, within native permissions, never inferred from repository files or
tool output. The Exploration coordinator carries the boundary too.

Docs (MISSIONS.md "Human questions and decisions", MISSIONS_RECURRING.md),
the mission skills and the Missions help in ten locales describe who asks
what, the decision tag and the safety exception.

Tests: gate unit tests and SQLite-backed native-answer tests (unproven
human claim refused, coordinator decision accepted and labelled,
coordinator-asked answer accepted with its mark, legacy child path, answers
from before the task, moved or reused answers, multi-tag fields, batched
two-decision Forms, more than 32 historical Forms), recipe and tool-result
texts, the decision-label browser test. Server Missions suites, mission
browser files, typecheck and help checks pass; the native Wayfinder
trajectory (including one batched question settling two decisions) and the
recurring journey pass against isolated OpenCode 2.2.3 and 2.2.5.
…er (#1226)

Playbooks were opaque: the Mission editor showed one sentence per playbook,
and nothing told users which steps and roles a playbook runs, when they are
asked something, what CodeNomad actually enforces, or which instructions
the agents receive.

A new help page, playbooks.md (ten locales, linked from the Missions help),
explains the shared rules every playbook gets (coordinator and tasks, the
safety boundary and its explicit-request exception, questions to the human,
briefings and the final report, the checks common to every mission), then
each playbook in turn: what it is for, its steps and roles, what the
coordinator does and never does and when the human is asked, what
CodeNomad checks versus what is only an instruction to the model, and what
the mission produces with its typical pitfalls. Every statement was checked
against the recipes and the server checks; decision tags and the
Coordinator decision / Your answer labels are scoped to Native subagents.

"See details" under the playbook description in the editor's Options opens
a read-only view in the Mission reader, beside the editor: steps, roles
with their localized labels, and, folded per recipient, the complete
English texts each agent receives. They come from a new read-only route,
GET /api/missions/playbooks?taskMode=, built by missionPlaybookDetails()
from the real prompt builders (buildAssignmentPrompt, buildActorContext)
with placeholders, so the view cannot drift from what agents get. The
button is disabled while a creation request is in flight and opening the
reader no longer cancels the editor's pending creation. A failed load
offers Retry, and a saved playbook reader is restored, with unknown ids
dropped.

Tests: a drift guard comparing whole sent texts with the export for native
and independent assignments, independent session context, and one-time and
daily coordinators of every playbook, with a negative check and a test
placing each question/safety rule on the right recipients; a route test;
browser tests for See details (including a delayed response) and for
opening it during a creation answered 200 and 400; view-state restoration.
Server, UI unit, mission browser, help structure and help browser tests and
the typechecks pass.
dependabot Bot and others added 6 commits November 8, 2026 15:29
…1325)

Update electron-vite 4.0.1 -> 6.0.0. electron-vite 6 requires Vite 6, 7 or 8 and its declarations use BuildEnvironmentOptions, absent from Vite 5 (the cause of the original TS2769 errors), so the Electron package declares Vite ^6.4.5 and its own vite-plugin-solid ^2.11.15; the standalone UI keeps Vite 5.

Lockfile regenerated normally from current dev with npm 11.19.1; every changed record matches the independently reviewed candidate. Typechecks, electron-vite production builds, preload tests and a Windows package smoke passed locally; full CI is green on the exact head (earlier attempts failed only on github.com release-download 502/504s, rerun after downloads recovered). Clears the Vite alerts on packages/electron-app/package.json.
… an icon (#1391)

## Summary

The creation form's subsession nesting depth was a block of prose:
- a heading and its info icon;
- the write target, the effective depth and a nesting sentence;
- the configuration file, often the same path again;
- a numeric field, then the nesting rule;
- a second rendering of the info text;
- an Inherit button.

It is now **one row**, like Task sessions.

- **Subagent depth ⓘ [menu]**: Inherit (naming the inherited depth while
no project override is set), then 1 to 10 within the capability's
maximum. A choice saves at once, so the Inherit button, the Enter/Escape
handling and the invalid-value state are gone.
- While an empty project `experimental` block hides the inherited depth,
the menu shows **OpenCode default**; choosing Inherit repairs it.
- **Everything else is in the one info tip**, one part per line: scope,
nesting rule, effective depth, nesting status and both paths (isolated
left-to-right). The shared tooltip now honours line breaks.
- Only actionable alerts and the unsupported-contract status stay under
the row. Saving and saved states are announced to assistive technology
only.
- Reads and writes lock the menu with `aria-disabled` (keeping focus)
and refuse a choice made meanwhile.
- In the creation form the row is part of **Delegation**, whose rows
share one label column, so both menus line up. Preferences shows the
same row.
- The creation footer's **Save as brief** is a bookmark icon named by
its tooltip.

![The Delegation group: Task sessions and Subagent depth as two aligned
rows, the bookmark icon in the footer (real dev
instance)](https://raw.githubusercontent.com/NeuralNomadsAI/CodeNomad/e14f3e7817b428394fc6dfbbf9eaa51dd421f69c/pr-assets/missions-depth-row.png)

![The depth info tip, one part per line (real dev
instance)](https://raw.githubusercontent.com/NeuralNomadsAI/CodeNomad/e14f3e7817b428394fc6dfbbf9eaa51dd421f69c/pr-assets/missions-depth-info.png)

## i18n, docs and help

- Ten locales: `missions.depth.label` shortened, `inheritValue` and
`masked` added, `title` and `invalidValue` removed; the hint no longer
says an empty value inherits.
- `dev-docs/MISSIONS.md` describes the row, its info and the icon.
- The Missions help is updated in ten locales.

## Validation

- Browser tests drive the menu:
  - the one-row layout (one info tip, no prose, no buttons);
  - the offered values and Inherit's label;
  - immediate saves;
  - locked reads and writes refusing a forced choice;
  - the empty-block repair;
  - the info lines (nesting status, paths);
  - uncertainty fencing across Locations;
  - the row inside Delegation and the footer icon.
- Full `mission-*` suite plus automation-forms and preferences-startup:
549/549.
- UI typecheck and `npm run test:help` pass.
…ometry (#1392)

The panel shows the selected mission's fallback row before the summaries inventory arrives, so counting rows right after the task rows rendered produced 1 instead of 12 on CI. Wait for the 12 inventory rows, then keep the exact count assertion. Holding only the summaries response reproduces the CI failure on the original test and passes with the wait; full geometry file 8/8; full CI green. No timeout, assertion or product change.
ci: serve release downloads from verified dev-seeded caches

github.com release downloads returned 502/504 intermittently for hours on 2026-11-08, failing "Install the Electron binary" in test and build jobs on every OS and Tauri bundling (AppImage runtime, Windows installer tooling). Every job re-downloaded these artifacts on each run.

A trusted seeding workflow (push to dev on lockfile/cache changes, twice-weekly schedule, manual dispatch; dev only, contents: read) fills dev-scoped caches for the four runner images: npm, Electron zips and electron-builder's fpm/winCodeSign, Cargo's downloaded crates and the AppImage runtime. Tauri bundler tools are saved only by the scheduled nightly dev build. A restore-only composite action re-verifies what a tool would otherwise trust: Electron zips against electron's checksums.json before app-builder sees them, .crate files against Cargo.lock, and the AppImage runtime against GitHub's published SHA-256. Pull-request and test-signing workflows declare cache-mode: read, so they restore but never create caches; this also stops the per-PR npm cache copies that pushed the repository over its 10 GB cache limit. Cargo target/ is deliberately not cached.

On a cache miss or helper failure every tool downloads exactly as before; no retry hides real failures, and no test, signing or verification step changes. Restore steps are skipped when a checked-out branch lacks the new action, so open PRs keep today's behaviour until they merge dev.

New download-cache tests (guards, ordering, restore-only, save conditions, verification) and the existing workflow/policy tests pass (55/55); strict YAML and actionlint are clean apart from actionlint's missing cache-mode support. Independent review found no findings.
Update tauri-winres 0.3.5 -> 0.3.7 with its TOML 1.x edge; the obsolete TOML 0.9 records become unreachable and are removed. The lock conflict with the plist refresh (#1366) was resolved against current dev, preserving every retained package, dependency edge, security version and the vendored Tao/updater path patches. Locked metadata, the two vendor checks and actual Windows resource-generation/PE metadata assertions pass. Full CI is green on the exact head; earlier failures were a since-fixed fixture race (#1380, #1362), github.com release-download 502/504s, and an unexplained Missions UI test unrelated to this lockfile.
## Summary

Prepare **CodeNomad 1.1.0** on dev `689f73b62`. Align the root, all five
workspaces, npm lock entries, Tauri configuration and Cargo package/lock
version. The npm lock differs only in seven release-version fields;
dependency versions and metadata are unchanged.

Set the separately published UI's **minimum server to 1.1.0**. This is a
consumed API boundary: its Missions store uses `POST
/api/workspaces/:id/missions/panel-page` for inventory and task pages,
absent from v1.0.1. Older servers retain their cached/bundled UI instead
of downloading the incompatible 1.1 UI. Test 1.0.0, 1.0.1, 1.1.0 and
future-server behavior; document the contract and update all ten help
locales.

## Validation

- Stable 1.1.0 and preview-family versioning plus clean offline
workspace installation/local remote-tunnel linking pass.
- Tauri/Cargo alignment and the remote UI server floor are checked.
- UI compatibility and release-PR-note contracts: **7/7** passed.
- Help checks: **31/31** passed across ten locales.
- Workspace typechecks and server/UI/native plugin build pass.
- npm package dry-run confirms `@neuralnomads/codenomad@1.1.0` and its
shipped CLI/Missions/pruning/memory entrypoints.
- Strict lockfile metadata comparison and whitespace checks pass.

## Publication boundary

This preparation creates no stable release or tag and changes no signing
or publishing credentials. After merge, prepare a **draft dev → main**
release PR whose body is the public release notes. Merging that separate
PR publishes GitHub/npm/UI/updater assets and requires explicit
publication approval. Existing nightly dev previews retain their normal
schedule and will use the new base version. Production Windows signing
remains disabled in current repository configuration.
@pascalandr

pascalandr commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Release preparation checkpoint ÔÇö keep this PR draft

Preparation #1394 is merged into dev as 414e2d29ea15553cd4c131fd5e5a11205be97986. Root/workspace/Tauri/Cargo metadata is 1.1.0, with dependency metadata unchanged. The remote UI server floor is 1.1.0, justified by the consumed Missions panel-page API and covered by old/current/future server tests.

Completed locally

  • Stable/preview versioning and offline clean workspace install/linking.
  • Tauri/Cargo version alignment and strict seven-field npm-lock comparison.
  • 7/7 UI compatibility/release-note contract tests; 31/31 all-locale help checks.
  • Workspace typechecks; server/UI/native plugin build.
  • npm package dry-run: @neuralnomads/codenomad@1.1.0, expected shipped CLI/Missions/pruning/memory entrypoints present.
  • Notes checked against merged changes since stable v1.0.1; unmerged proposals and local main-checkout work excluded.

Before publication

  • Review the exact final dev head and release notes; this PR follows dev as it advances.
  • Confirm the exact final release-candidate/platform CI and packaging. Historical preparation result: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/42504890301 completed successfully on attempt 1, including validation and platform builds. Tested source b91d82befd2fb38d4d82b9de6f2491d4686ff4ea is byte-identical to merge 414e2d29ea15553cd4c131fd5e5a11205be97986, which was the preparation checkpoint head. Subsequent dev changes require fresh final-head checks.
  • Decide the Windows production-signing policy: SIGNPATH_RELEASE_SIGNING was absent at preparation, so stable Windows assets remain unsigned by Authenticode. Preserve that state unless the production certificate is confirmed ready and activation is explicitly approved. Never use the test certificate for stable assets. Tauri updater signatures are a separate mechanism.
  • Give explicit publication approval before making this PR ready and merging it to main. That push automatically creates the GitHub release/tag and publishes npm, remote UI and verified updater artifacts.

GitHub administrator access is verified. Stable npm 1.0.1 was published by GitHub Actions/OIDC through the unchanged publishing workflow; the public npm maintainer is codenomadnpm. Existing nightly dev previews retain their schedule and use the new base version. No stable tag/release, publishing dispatch, signing activation or shared-service/user-configuration mutation was performed during preparation.

The PR body is intentionally only the public release notes, because the stable workflow copies that body into the release. This comment holds operational gates instead.

Archive CI follow-up

Archive CI 42538167720 is red. Both native archive probes (2.2.3/2.2.6) and both archive browser journeys passed. Remaining observed failures are the Cargo lockfile (#1407), the build prerequisite test (correction in #1305), asynchronous Tauri help receipts (#1410), and an Electron preview key/readiness symptom (tracked by #1345; exact cause not proven). WebKit fixture wiring was fixed in merged #1402. These existing follow-ups must be reviewed/integrated and the exact final candidate validated before the CI gate can be checked. No stable publication or production-signing activation is authorized by this checkpoint.

…source-map uploads (#1397)

build(cloudflare): update Wrangler to 4.158 and keep deploys without source-map uploads

The release worker's tooling was 98 Wrangler minor releases behind, and its lockfile carried 26 open advisories through Wrangler's own dependencies (Undici 7.18.2, Sharp 0.34.5, ws 8.18.0). Update Wrangler to 4.158.0 and regenerate the Cloudflare lockfile normally with npm 11.19.1; it now resolves Undici 7.30.1, Sharp 0.35.5 and ws 8.21.0, and a fresh audit reports no vulnerabilities.

Wrangler 4.150 enabled source-map upload on deploy by default. Set upload_source_maps = false so this tooling update does not change what production deploys upload; the dry-run output again contains only the bundle. The compatibility date and the rest of the configuration are unchanged. Wrangler 4.158 ships Miniflare 5 (beta); this package only drives the Wrangler CLI and has no direct Miniflare API consumer.

Frozen install, full dependency-tree validation, registry signatures, both Wrangler dry-run deploy forms, the Worker behaviour tests, real Miniflare/workerd execution, the workerd version check and native/WASM Sharp checks pass on Windows x64 with Node 24.22.2 (Wrangler now requires Node 22+). No real deployment was performed.
…sts (#1396)

Two Missions UI browser tests fail intermittently on CI and could not be reproduced locally (0/10 normal, 0/10 under 4x CPU throttling): the lifecycle Play/Pause/Resume test timing out waiting for an enabled Resume control after a panel refresh, and the recurrence late-refusal test where the selected row's Pause never reaches the route.

Record a bounded, passive event log (mission requests and outcomes, fixture control replies, pointer/click targets with the eligibility of the hit control, row identities, notices, console errors) and emit it as a single failure-only test diagnostic under 16 KB. Successful runs produce no output. Every assertion, wait, timeout and retry is textually unchanged. Full CI is green; the first attempt failed only on the separate automation-preview "Key:a" flake.
The projection-only delta-churn fixture let an unrelated session-history outline request escape to a generic API response; real native event projection can cancel that obsolete request, which then failed the strict page-error collector (net::ERR_ABORTED) after the DOM identity assertions had passed. This failed several unrelated PRs (most recently #1398).

Serve the existing valid empty-outline adapter before mounting the Solid components and explicitly reject any outline HTTP escape. Every browser-error collector, native event dispatch, action-eligibility and DOM identity/mutation assertion is unchanged. Merged current dev cleanly (both files unchanged there); full CI is green on the exact head.
… and mdast-util-to-hast copies (#1398)

build(deps): refresh vulnerable glob, minimatch, brace-expansion, jws and mdast-util-to-hast copies

Open Dependabot alerts on the root lockfile included runtime-scope advisories in brace-expansion, minimatch, glob, jws and mdast-util-to-hast whose patched releases are already allowed by every parent's declared range. Refresh those copies in the lockfile only, with npm 11.19.1 (the CI npm), without manifest changes, overrides or forced resolution.

Each changed copy moves within its major: brace-expansion 1.1.21 / 2.1.7, minimatch 3.1.5 / 5.1.9 / 9.0.9 / 10.2.6, glob 10.5.0, jws 4.0.1 and mdast-util-to-hast 13.2.1. minimatch 10.2 itself replaces @isaacs/brace-expansion with brace-expansion 5. Eighteen records change, two are added and two removed; the other 1,268 records are byte-identical. This clears 18 alerts (12 runtime, 6 development). The minimatch and glob copies whose parents pin an older range, and KaTeX (needs a manifest change), remain for later updates.

A frozen install succeeds and npm ls reports exactly dev's existing problems. Server, UI and Electron typechecks, server and UI builds, the 36 Markdown tests, the 8 resource tests and the consumers of the changed packages pass.
## Scope

Native browser previews, agent browser automation parity, and preview
capabilities across **Electron, Tauri Windows, Tauri macOS and Tauri
Linux**.

- Shared native embedding, geometry, overlay masking, session-owned
history and profile-scoped preview storage, isolated from the trusted
application renderer.
- Public `codenomad.browser`: open, navigate, snapshot, click, type,
select, press, scroll, hover, drag, wait and screenshot through the
authenticated backend and fenced native-parent channel.
- Six native viewport presets, WebKit HTTP/JavaScript mobile identity,
reset/reload and per-registration isolation. Native WebKit density/input
differences remain explicit.
- OAuth pop-ups with opener/postMessage and shared preview storage;
downloads with sanitized unique names; native dialogs and
owner/opener/renderer lifetime cleanup.
- Camera, microphone, location and notifications remain denied. Other
classified permissions retain native consent/selection and fail-closed
attribution. Guests receive no application privileges, app IPC, page
Node access or general evaluation bridge.
- Current-state developer documentation and user help in ten languages.

**Tauri macOS requires macOS 14 or newer; Electron keeps its own
platform baseline.**

## Host matrix

| Host | Native preview/history | Agent automation | Mobile profiles |
Pop-ups/downloads/dialogs |
| --- | --- | --- | --- | --- |
| Electron | Chromium | Supported | Chromium | Supported |
| Tauri Windows | WebView2 | Supported | Chromium | Supported |
| Tauri macOS 14+ | WKWebView | Supported | Native WebKit | Supported |
| Tauri Linux | WebKitGTK | Supported | Native WebKit | Supported |

## Qualification

- **Ubuntu 24.04 KDE VM:** real application built from the merged
sources, real backend and parent channel, private OpenCode daemon and
deterministic provider producing actual agent tool calls. Full browser
action sequence, OAuth opener messaging, downloaded-file contents,
native dialog acceptance and PNG passed under X11/Xwayland. Additional
native Wayland qualification uses an owned private KWin compositor;
native dialog operator coverage is recorded separately.
- **macOS 15.8 Intel VM:** same real-application/agent-tool path, all
actions and capability smoke passed with the built UI, private
profile/configuration and private OpenCode daemon. The app-window
nested-scroll check also qualifies the Cocoa wheel-coordinate
correction.
- **Windows:** Tauri release executable under its own
profile/configuration; all actions, OAuth, completed download, native
dialog and PNG passed through actual agent tool calls. The user's main
CodeNomad and shared OpenCode service remain untouched.
- Native CI qualifies Linux, macOS Intel, macOS ARM64 and Windows.
Authorized draft qualification uses no signing secrets.
- A **real Mac trial remains a release qualification step**. Graphical
macOS ARM64 and physical hardware capabilities retain their own
qualification scope; the capability matrix describes supported contracts
rather than exhaustive peripheral testing.

## Documented limits

- macOS first standalone hover can omit `pointerenter`; ⌘A selects
through the native responder while consuming the main A keydown.
- WebKit captures script-created closed shadow roots; declarative closed
shadow roots remain opaque.
- WebKitGTK preserves CVE-2025-13947 dropped-file protection; use the
page's native file input. No dropped URI text is converted into host
file access.
- GTK async clipboard reads lack requesting-frame attribution and are
refused; native Ctrl+V and clipboard writing remain available.
- WebKit keeps native screen/platform/pointer/hover/touch metadata.
WKWebView uses DPR 2.75 when its runtime-checked native selector exists;
GTK DPR follows monitor scale × fitted zoom. CSS dimensions remain
stable across app zoom/DPI. No JavaScript getter shims.
- Cross-origin frame contents retain the documented opaque automation
boundary. Page input timeouts have unknown outcomes and are never
replayed automatically.

## Integration

- Merge `ef69b4085`: `feat/tauri-webkit-parity`.
- Merge `e0d56d428`: `feat/tauri-webkit-capabilities` (`d58022b13`).
- Activation `3c68ab566`; Cocoa wheel correction `ecf5751ec`.
- The fetched `origin/dev` merges cleanly with this head. Pushed history
is preserved; no force-push or merge into `dev`.

The shared embedded-view layer remains available to remote-tabs work
while each consumer retains its own origin/profile/login/pairing/events
policy.
…budget (#1400)

fix(client-state): let one slow identity helper use the whole lookup budget

The Electron Node host and the Tauri Rust host read a process's birth identity on Windows (and macOS) through a short-lived helper (PowerShell on Windows), with two attempts of five seconds each. Under heavy CPU contention a cold PowerShell start exceeds five seconds; both attempts time out, the host reports no identity, and cross-host election correctly fails closed, so the Node host never becomes primary. This is the intermittent Windows CI failure in client_state::cross_host::tests (cross_host.rs:1135 / :1068, about 10.4-11 s per failure), reproduced locally under controlled contention with real ETIMEDOUT results.

Keep the same nominal ten-second total and the same two attempts, but share one deadline: a slow first helper may use the whole budget, and a retry only gets what remains. Identity semantics, the helpers and fail-closed behaviour on genuine failure are unchanged. This is a mitigation of slow cold starts, not a removal of the helper: extreme contention can still exceed the total budget.

New tests cover a six-second cold helper succeeding, a retry receiving only the remaining budget, and budget exhaustion staying unavailable without starting another helper; they fail on the previous code. The cross_host Rust module passes 0/10 failures normally and 0/6 under moderate load; existing test bodies and assertions are unchanged.
…1404)

## Summary

- Extend the existing `SettingsInfoTip` with `help?: { page: string;
anchor?: string }`; retain short hover/focus explanations and open
matching localized help externally on click, Enter or touch.
- Reuse `helpUrl` and `openExternalUrl`, guard page/anchor syntax,
preserve containing controls and disclosures, and show the existing
localized help error without replaying failed actions.
- Carry destinations through existing choice rows, behavior settings and
core Status metadata; document the reusable pattern and update the
settings guide in all ten languages.

## Linked inventory

- General: automatic subsession expansion, keep-awake, Memory, Tidy up
and memory-review warning.
- Advanced: whitespace-only changes and session environment variables.
- OpenCode: startup launch.
- Providers: image generation and image storage.
- Notifications: On screen, History and System column tips.
- Speech: provider, separate providers, shared/input/output base URLs,
playback mode, output format, playback test and prompt voice input.
- Status: Tokens, Yolo mode, Usage, Background Shells, MCP and Plugins.

Mission creation/scheduling/depth/preferences belong to the separate
form change, which can use the shared prop directly. Dynamic mission
budget, attention and diagnostics tips retain their information-only
purpose. Existing `SettingsInfoLink` and integration `InfoPopoverButton`
explanations retain their actionable/runtime-specific popovers; sidebar
Info navigation and Info-card heading icons keep their distinct purpose.

## Validation

- `npm ci` in the isolated checkout.
- UI typecheck.
- URL/section checks: every literal contextual destination resolves in
all ten locales, including `zh-Hans`.
- Behavior and right-panel registry tests: 14 passed.
- Full help suite: 31 passed, validating 28 pages × 10 locales.
- Real Solid browser gesture and relevant settings regressions: 56
passed (new shared-tip cases, startup launch, General/appearance,
Memory, notifications and help-command routes). All eight new
gesture/native-route cases pass.
- The fixture-wiring audit reports two existing WebKit fixtures without
the required exemption/navigation-budget wiring; those files and the
audit inputs are unchanged from `origin/dev`.

## Scope

No Mission form/tree edits, new dependencies, URL registry, plugin
extension point or duplicated info component. Visible explanations and
opener errors reuse existing translations.

## Independent review and CI

Round 1 found a translation-readiness race in the browser test. The test
now waits for the real localized info-link label before activating it.
Round 2: **No findings**.

Current `origin/dev` is merged. CI has baseline failures in the
unchanged binary-build ordering expectation and Cargo lockfile; the unit
failure reproduces locally and the affected inputs match `origin/dev`.
The existing fixture-wiring audit likewise reports only unchanged WebKit
files. These are tracked in the PR discussion separately from
contextual-help validation. Additional platform jobs remain
queued/running; admin merge is explicitly authorized for this change.

## Real component capture

![Startup and keep-awake settings with the shared contextual info
tooltip](https://raw.githubusercontent.com/NeuralNomadsAI/CodeNomad/95c2ada0f/pr-assets/contextual-help.png)
## Summary

Closes #929 and #1207 by using the native archive/restore API already
present in OpenCode 2.2.3. The 2.2.6 `run --archived` announcement adds
CLI convenience; no minimum or dependency bump is required.

- Session row **Archive session / Restore session** actions and an
**Archived** badge.
- Search-local **Unarchived sessions / Archived sessions / All
sessions** selector.
- Hide idle archived branches in normal browsing and session-list
cycling; preserve selected conversations and paths to running work or
pending requests. Children retain independent native flags and remain
independently searchable.
- Serialize native writes, follow with ordinary authoritative inventory
refresh and verify the flag. Failed/uncertain writes never synthesize
local archive state or replay automatically.
- Project archive events and fence older metadata reads; retain complete
native inventory for ownership and recovery.
- Technical docs and help in all ten locales; native minimum/latest
coverage in CI.

## Validation

- Official isolated **2.2.6 and 2.2.3** CLI probes pass archive/restore,
filtered cursor pagination, unfiltered listing, persisted flag,
idempotent events, retained transcript and unchanged child flag, with no
archive-induced model execution.
- **132** targeted UI/store/recovery/pagination tests pass; **61** proxy
ownership tests pass.
- Workspace typechecks and **31** help tests pass.
- Main real-browser archive/restore/search/failure journey passes, as do
existing search and child-activity checks. Final compact-menu/delete
reruns are in progress.
- The repository fixture-wiring checker reports two pre-existing
violations in unchanged WebKit automation tests
(`automation-webkit-geometry.test.ts`,
`automation-webkit-page.test.ts`); the new browser fixture uses the
shared daemon and navigation budget.

Native probes use synthetic private databases and a local provider,
never the shared daemon or user state. Large existing touched files:
`session-list.tsx` (~1168 lines), `session-events.ts` (~732),
`session-state.ts` (~1343).
## Summary

Follow-up to #1318: the delivered Missions tree diverged from the
session list it was modelled on. This brings it in line with the issue's
design.

- **One selection, session-list highlight.** The panel selects a row
**or** one task, never both. A click only selects; only the chevron (the
session list's own `SessionExpander`) expands. Expanded rows are kept
per project. A Mission without tasks has no chevron: summaries now carry
an optional `taskCount`.
- **Tasks are session-list rows**, nested one level, with the same
height, separators, rollover and highlight.
- **The eye is a mode.** Pressed, the central reader shows the selection
(a Mission's overview, or the task's reader) and follows each new
selection. Released, it closes. A selected task takes the header
toolbar.
- **Colours agree.** The chevron uses the task rows' own states
(`missionTaskRowState`): input, then active, then unseen finished.
- Finished tasks stay **green until seen**. Selecting one, clicking its
icon or sweeping across icons greys it. The chevron's right-click **Mark
as read** clears a Mission's tasks and its own completion.
  - Tasks not started yet are **blue**.
- **No text under rows.**
- Command progress and errors are announced and appended to the primary
button's tooltip; their actions stay visible.
  - The empty-plan and loading lines are gone.
- Delete happens at once. Deleting task conversations is a Settings ›
Missions checkbox, and the delete form only appears on failure.
- **Dependency rail.** Lanes start under the Mission chevron with a
fixed step. Rows move over when the lanes need more room, instead of
lanes merging at the panel edge.

![A task selected under its Mission, the eye pressed and the reader
following it, the dependency rail under the chevron (real dev
instance)](https://raw.githubusercontent.com/NeuralNomadsAI/CodeNomad/53d37ad19ed2994b9df859fa338f4c31a09b4726/pr-assets/missions-tree-parity.png)

![Task rows as session-list rows with a single highlight (real dev
instance)](https://raw.githubusercontent.com/NeuralNomadsAI/CodeNomad/53d37ad19ed2994b9df859fa338f4c31a09b4726/pr-assets/missions-tree-task-selected.png)

Not in this PR: a recurring Mission listing its passages as children
(#1318) needs a passage listing the panel does not have yet. It follows
separately.

## Docs

- `dev-docs/MISSIONS.md` describes the selection, expansion, eye,
colour, seen and deletion model.
- The Missions help (ten locales) covers the same.

## Validation

- Missions browser tests migrated to the new behaviour.
- New `mission-tree-parity.test.ts`, every test failing on the previous
UI:
  - single highlight;
  - chevron-only expansion;
  - the eye following the selection;
  - chevron colours from task states;
  - unseen tasks and Mark as read;
  - leaf Missions;
  - immediate delete honouring the preference;
  - feedback in the tooltip.
- Missions, session and automation browser suites: 669/670. The
schedule-list cold-start timeout passes alone (37/37).
- Server mission tests, plus a `taskCount` test.
- UI and server typecheck; `npm run test:help`.
- `tests/browser/mission-control.test.ts` is about 1,700 lines.
## Summary
- Enable the existing free-model Console calibration by default: show
`max(local steps, Console free-model requests since UTC midnight)` when
the active Console connection is usable.
- Keep the `Estimated` label, local fallback, exhaustion marks and
display-only scope. Account selection is unchanged.
- Reserve time for local publication if the optional Console/credential
read stalls; one caller timing out does not cancel another caller's
shared transport.

## Scope and limitations
Console account-wide totals can include other clients and IP addresses,
while the 200-request reference is IP-based. Whether Console counts
authenticated free requests remains unconfirmed; a zero or lower Console
count leaves the local estimate unchanged. No real user credentials or
live Console calls are used in tests. Unknown local counts remain
unknown.

## Documentation
Updated the existing information popover and `docs/help/*/providers.md`
in all ten locales, plus `dev-docs/PROVIDER_ACCOUNTS.md`.

## Validation
- Usage/provider/rotation/route tests: 289 passed, no skipped tests.
- Focused free calibration + Usage/Google routes: 70 passed.
- Chromium free-model information popover and provider switch: passed;
French formatting and UTC-midnight cases also passed.
- Localization navigation-label checks: 10 passed.
- Server and UI typechecks: passed.
- Help checks: 31 passed (28 pages × 10 locales); help build passed.
- Independent review found and prompted fixes for stalled optional reads
and an unmocked stats-only test. Follow-up review in progress.
…1405)

## Summary
- Put reusable configurations first and preserve their existing
version-1 records, notes and requested profiles. Rename the UI/help
terminology in all ten locales.
- Show compact preparation controls directly, retain one-row growing
Objective/Notes fields, and use contextual localized help instead of
inline playbook prose or See details.
- Add **Start on creation**, off by default. Only a direct confirmed
creation response continues the existing guarded lifecycle/schedule
operation; recovered or uncertain acknowledgements never autostart.
- Make **Once, now** the initial composable criterion. Add a criterion
appends a second row. Existing or/if/else groups, source checkpoints,
native calendars and exact request receipts decide admission.

## Safety and compatibility
Sole-now preparations retain ordinary one-time journal admission and
native-default profiles. Mixed policies freeze a single existing native
`at` calendar when armed, preserve its consumed-slot evidence through
restart/replay/timing edits, and allow an unmet condition to consume the
sole chance. Existing stored launch policies remain readable without
automatic migration. OpenCode itself is unchanged.

## Integration
Contextual-help #1404 is integrated. **Do not merge before #1402**; its
tree and auto-delete changes will be merged normally before the final
review gate.

## Validation
- `npm ci` and `npx playwright install chromium`
- 161 focused server scheduling/preparation/calendar-owner tests
- UI scheduling/data/decode/summary: 10; reusable-library records: 14;
creation helper/draft tests: 10
- Direct scheduled-create/activation/lost-acknowledgement browser
fixture: 5 tests
- Focused real-editor browser rerun: 12 tests; broader regressions and
final shared-help checks are being completed
- UI/server typechecks; production UI build; Missions plugin build
- Help: 31 tests, validating 28 pages in all 10 locales; i18n: 7 tests;
contextual destination tests: 4

The untouched baseline fixture-wiring check reports existing violations
in `automation-webkit-geometry.test.ts` and
`automation-webkit-page.test.ts`; the new fixtures pass its wiring
requirements. A real-component fixture screenshot and independent
read-only review results will be attached before merge.
Agent-prepared missions should use the same new-mission preferences as the Missions editor. Fill missing coordinator, role and task-mode choices through shared resolution, preserve explicit whole-slot selections, and expose recorded selections and their origins in tool responses.

Read the owning backend's authoritative preferences over the authenticated bridge and refuse invalid preferences with actionable errors. Keep standalone inspect-start creation available with explicit unavailable-preference provenance. Resolve preferences outside the project mutation lock and retain first-recorded choices across retries, restarts and preference changes.

Persist bounded preparation intents and provenance through native RPC, journals and unified documents. Keep strict ownership discovery separate from ordinary worktree behavior, preserve Compare judge inheritance, and direct out-of-mission preparation to the Missions editor. Update tool guidance and help in all ten locales.

Validated focused creation, automation, ownership, route and replay regressions, both typechecks, native bundle builds and help checks. Independent agent reviews identified issues addressed in the branch; the review record and final integration results are recorded on the PR. Existing parallel test failures passed their serial rerun. The OpenCode 2.2.6 scheduled-wake fix from dev is preserved.

This branch was successfully deployed

1 active deployment
github-pages — 16104c23 Deployed Nov 9, 2026 by pascalandr via deploy #751
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants